Strange NSFormatter behavior - objective-c

I have TextField with value binded to Document's serverAddress property (readwrite, copy), and TextField formatter delegate connected with ServerAddressFormatter object in XIB.
It's actually working with input like 127.0.0.1:8080, but as soon as I put something without : TextField clear itself completely.
Here's ServerAddressFormatter implementation:
#implementation ServerAddressFormatter
- (NSString *) stringForObjectValue:(NSArray *)obj {
if ([obj isKindOfClass:[NSArray class]]) {
return [obj componentsJoinedByString:#":"];
} else {
return #"";
}
}
- (BOOL)getObjectValue:(out id *)anObject
forString:(NSString *)string
errorDescription:(out NSString **)error {
int i;
for (i = (int) ([string length] == 0 ? 0 : [string length] - 1); i > 0; i--) {
if ([string characterAtIndex:i] == ':') break;
}
if (i == 0) {
*anObject = #[string]; // if I put string, #"100" here it's working fine
} else {
*anObject = #[[string substringToIndex:i], [string substringFromIndex:i+1]];
}
return YES;
}
#end

Related

How to check if an object is the last object on NSArray

How can I check if an object in the last object on an NSArray?
I've tried:
if ([currentStore isEqual:[Stores lastObject]])
{
//Code
}
but it didn't work.
Any idea?
Thanks!
or try this
BOOL lastElement = false;
NSUInteger index = [stores indexOfObject:currentStore];
if (index != NSNotFound)
{
lastElement = (index == [stores count] - 1);
}
Bit modified try this:
NSUInteger index = [stores indexOfObject:currentStore];
if (index == ([stores count]-1))
{
NSLog(#"Yes its Last");
}
If you didn't override isEqual method, the base class implementation of NSObject::isEqual only check if both pointers points to the same address.
This excellent article http://nshipster.com/equality/ explain objc equality principles
The below sample logs - Testing Stores - works fine
#interface Stores : NSObject
#property (strong, nonatomic) NSString* name;
- (instancetype) initWithName:(NSString*) name;
#end
#implementation Stores
- (instancetype) initWithName:(NSString*) name;
{
_name = name;
return self;
}
- (BOOL)isEqualToStores:(Stores*) Stores
{
if (!Stores)
return NO;
if (![_name isEqualToString:Stores.name] )
return NO;
return YES;
}
- (BOOL)isEqual:(id)object
{
if (self == object)
{
return YES;
}
if (![object isKindOfClass:[Stores class]])
{
return NO;
}
return [self isEqualToStores:(Stores *)object];
}
#end
-(void) testStores
{
Stores* last = [[Stores alloc] initWithName:#"5"];
NSArray* arr = #[
[[Stores alloc] initWithName:#"1"],
[[Stores alloc] initWithName:#"2"],
[[Stores alloc] initWithName:#"3"],
[[Stores alloc] initWithName:#"4"],
[[Stores alloc] initWithName:#"5"]
//last
];
if ([last isEqual:[arr lastObject]])
{
NSLog(#"Testing Stores - works fine");
}
else
{
NSLog(#"Testing Stores - opps!?1?!?");
}
}

Making autocomplete case-insensitive

I'm implementing an autocomplete in cocoa for an OSX application and thus far I've got it all pinned down. The one hangup is that the autocomplete is case-sensitive and that's not really what I want/need. Ideally the autocomplete will be case INSENSITIVE. Relevant code below:
#implementation autocompleteController
- (void)viewDidLoad {
[super viewDidLoad];
self.textField.delegate = self;
}
-(void)controlTextDidChange:(NSNotification *)obj{
NSTextView * fieldEditor = [[obj userInfo] objectForKey:#"NSFieldEditor"];
if (self.isAutocompleting == NO && !self.backspaceKey) {
self.isAutocompleting = YES;
self.lastEntry = [[[fieldEditor string] capitalizedString] copy];
[fieldEditor complete:nil];
self.isAutocompleting = NO;
}
if (self.backspaceKey) {
self.backspaceKey = NO;
}
}
-(NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index{
NSMutableArray * suggestions = [NSMutableArray array];
NSArray * possibleStrings = #[#"TEST", #"ABC", #"abc", #"amauroy", #"AMA", #"amazing"];
if (!self.lastEntry || !possibleStrings) {
return nil;
}
for (NSString * string in possibleStrings) {
if ([string hasPrefix:self.lastEntry]) {
[suggestions addObject:string];
}
}
return suggestions;
}
-(BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector{
if (commandSelector == #selector(deleteBackward:)) {
self.backspaceKey = YES;
}
return NO;
}
#end
As Pro Blaster points out, the problem is with the following line:
if ([string hasPrefix:self.lastEntry]) {
Your autocompletion is case-sensitive because -hasPrefix: is case-sensitive. One approach is to convert everything to lower case (upper case would also work, of course). Another is to write a case-insensitive version of -hasPrefix: and add it to NSString using a category, like this:
#interface NSString (autocomplete)
- (BOOL)hasPrefixIgnoringCase:(NSString*)aString;
#end;
#implementation NSString (autocomplete)
- (BOOL)hasPrefixIgnoringCase:(NSString*)aString
{
NSRange *prefix = [self rangeOfString:aString options:NSCaseInsensitiveSearch];
return prefix.location == 0 && prefix.length == aString.length;
}
#end
Then use that method in your code:
if ([string hasPrefixIgnoringCase:self.lastEntry]) {
Note: The provided code is untested. The concept is sound, but you may find a syntax error or two.
I did this once.
You would do so by replacing :
for (NSString * string in possibleStrings) {
if ([string hasPrefix:self.lastEntry]) {
[suggestions addObject:string];
}
}
return suggestions
with:
for (NSString * string in possibleStrings) {
if ([[string lowercaseString] hasPrefix:[self.lastEntry lowercaseString]]) {
[suggestions addObject:string];
}
}
return suggestions;

How to restrict numbers and special characters in objective-c [duplicate]

This question already has answers here:
Restrict NSTextField to only allow numbers
(10 answers)
Closed 8 years ago.
In textfield I want to restrict numbers like (1234567890) and special characters but I want to allow alphanumeric characters. How I am suppose to do this?
Use the UITextField delegate method
textField:shouldChangeCharactersInRange:replacementString:
To check the string that is about to be replaced, if you allow it then return yes if not then return no.
Here is some more information.
Apple UITextField Delegate
try following code
+ (BOOL)isNumber:(NSString *)value {
if ( (value == nil) || ([#"" isEqualToString:value]) ) {
return NO;
}
int l = [value length];
BOOL b = NO;
for (int i = 0; i < l; i++) {
NSString *str =
[[value substringFromIndex:i]
substringToIndex:1];
const char *c =
[str cStringUsingEncoding:
NSASCIIStringEncoding];
if ( c == NULL ) {
b = NO;
break;
}
if ((c[0] >= 0x30) && (c[0] <= 0x39)) {
b = YES;
} else {
b = NO;
break;
}
}
if (b) {
return YES;
} else {
return NO;
}
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ( (string != nil) && (string != #"") ) {
if (![self isNumber:string]) {
return NO;
}
}
return YES;
}
You need to write a NSFormatter and assign it to your text field. Here an example implementation of a such NSFormatter which uses a NSRegularExpression to validate the NSTextField contents.
#interface XXNameElementFormatter : NSFormatter
#end
#implementation HcNameElementFormatter {
NSRegularExpression *_re;
}
- (id)init {
self = [super init];
if (self) {
[self initRegularExpression];
}
return self;
}
- (void)awakeFromNib
{
[self initRegularExpression];
}
- (void)initRegularExpression
{
NSError *reError;
_re = [NSRegularExpression regularExpressionWithPattern:#"^[a-z]*$" options:NSRegularExpressionCaseInsensitive error:&reError];
NSAssert(_re != nil, #"Error in regular expression, error: %#", reError);
}
- (NSString *)stringForObjectValue:(id)obj
{
return obj;
}
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
*obj = string;
return YES;
}
- (BOOL)isPartialStringValid:(NSString *__autoreleasing *)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString *__autoreleasing *)error
{
NSParameterAssert(partialStringPtr != nil);
NSString *partialString = *partialStringPtr;
NSRange firstMatch = [_re rangeOfFirstMatchInString:*partialStringPtr options:0 range:NSMakeRange(0, partialString.length)];
return firstMatch.location != NSNotFound;
}
#end

Print a line 10 times in Objective-C?

I know that print line is NSLog. How do I print a line 10 times in Objective-C?
I haven't had a chance to refactor yet, but something like this should work!
for(int i = 0; i < 10; i++)
{
if(i == 0)
NSLog(#"A line");
else if (i == 1)
NSLog(#"A line");
else if (i == 2)
NSLog(#"A line");
else if (i == 3)
NSLog(#"A line");
else if (i == 4)
NSLog(#"A line");
else if (i == 5)
NSLog(#"A line");
else if (i == 6)
NSLog(#"A line");
else if (i == 7)
NSLog(#"A line");
else if (i == 8)
NSLog(#"A line");
else if (i == 9)
NSLog(#"A line");
}
Another while loop option.
//
// main.m
// Pritner
//
// Created by Joshua Caswell on 7/19/12.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// A Pritner instance holds and displays a passed-in string. The string is publicly
// unchangeable.
#interface Pritner : NSObject
+ (id)pritnerWithLine: (NSString *)newLine;
- (void)printLine;
#property (readonly, copy, nonatomic) NSString * line;
#end
// Extension to manage "class variable" for counting number of created instances
#interface Pritner ()
+ (NSUInteger)numPritnersCreated;
+ (void)setNumPritnersCreated:(NSUInteger)n;
+ (NSUInteger)maxNumPritners;
#property (readwrite, copy, nonatomic) NSString * line;
- (id)initWithLine: (NSString *)line;
#end
#implementation Pritner
#synthesize line;
+ (id)pritnerWithLine: (NSString *)newLine {
id newInstance = [[self alloc] initWithLine:newLine];
if( newInstance ){
NSUInteger createdSoFar = [self numPritnersCreated];
// Only allow maxNumPritners to ever be created; keeping track of them
// is the client's problem.
if( createdSoFar >= [self maxNumPritners] ){
abort();
}
[self setNumPritnersCreated:createdSoFar + 1];
}
return newInstance;
}
// Fake class variable using associated objects; keep count of created instances
char numPritnerKey;
+ (NSUInteger)numPritnersCreated {
NSNumber * n = objc_getAssociatedObject(self, &numPritnerKey);
if( !n ){
n = [NSNumber numberWithUnsignedInteger:0];
[self setNumPritnersCreated:0];
}
return [n unsignedIntegerValue];
}
+ (void)setNumPritnersCreated:(NSUInteger)n {
objc_setAssociatedObject(self,
&numPritnerKey,
[NSNumber numberWithUnsignedInteger:n],
OBJC_ASSOCIATION_RETAIN);
}
// Maximum number of instances ever allowed to be created
+ (NSUInteger)maxNumPritners {
return 10;
}
- (id)initWithLine: (NSString *)newLine {
self = [super init];
if( !self ) return nil;
line = [newLine copy];
return self;
}
- (void)printLine {
NSLog(#"%#", [self line]);
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
while( YES ){
Pritner * p = [Pritner pritnerWithLine:#"I figure, if you're going to build a time machine out of a car, why not do it with some style?"];
[p printLine];
}
}
return 0;
}
Please don't use this in real life.
Don't forget the "object" in "Objective-C".
NSArray *lines = [#"123456789" componentsSeparatedByCharactersInSet:[NSCharacterSet alphanumericCharacterSet]];
[lines enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL* stop) {
NSLog(#"a line");
}];
If you think Objective-C isn't true enough to its Smalltalk roots, you can do something like the following.
typedef void (^LoopBlock)(NSNumber*);
#interface NSNumber (loop)
-(void)to:(NSNumber*)upTo do:(LoopBlock)block;
-(void)timesRepeat:(LoopBlock)block;
#end
#implementation NSNumber (loop)
static NSString *loopSeparator = #"_";
-(void)to:(NSNumber*)upTo do:(LoopBlock)block {
[ [ [#"" stringByPaddingToLength:[upTo unsignedIntegerValue]-[self unsignedIntegerValue]
withString:loopSeparator
startingAtIndex:0
] componentsSeparatedByString:loopSeparator
] enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL* stop) {
block([NSNumber numberWithUnsignedInteger:i+[self unsignedIntegerValue]]);
}
];
}
-(void)timesRepeat:(LoopBlock)block {
[[NSNumber numberWithUnsignedInteger: 1] to:self do:block];
}
#end
int main() {
#autoreleasepool {
[[NSNumber numberWithInt:10]
timesRepeat:^(NSNumber* i){
NSLog(#"a line");
}];
}
}
Use recursion:
print(10);
void print(int i){
if(i == 0)
return;
NSLog(#"print this line");
print(i - 1);
}
Use Grand Central Dispatch another way:
dispatch_apply(10, dispatch_get_main_queue(), ^(size_t curr_iteration){
NSLog(#"a line");
});
Use a goto statement:
int i = 0;
print:
NSLog(#"print this line");
if (i++ < 10) goto print;
Use a for loop:
for (int i = 0; i < 10; i++) {
NSLog(#"print this line");
}
Use Grand Central Dispatch:
__block int i = 0;
__block dispatch_block_t print_block = ^() {
NSLog(#"print this line");
i += 1;
if (i < 10) dispatch_sync(dispatch_get_main_queue(), print_block);
}
dispatch_sync(dispatch_get_main_queue(), print_block);
Use a while loop:
int i = 0;
while (i < 10) {
NSLog(#"print this line");
i += 1;
}

Objective C newbie

I'm taking a class and we're working on a Calculator program. My background is in C++. I am taking a RPN calculator entry of 3 enter sqrt and need to display it as sqrt(3) in my descriptionOfProgram method, which is new, including associated property below. Here's the class so far. Search for "xcode" to find my issues. Any ideas? I'm not very good at the basic objective c classes, but I'm trying to learn. Here's a summary:
it's complaining about my boolean. I'm not sure why. I did this in a different class and it worked fine.
it's looking for a { I don't see it
it doesn't like my use of the key. I'm unclear on how to get the key's contents I think is the problem.
It wants ] but I'm not seeing why
skipped
It expected } at #end
Hope you can help! Thanks!
//
// CalculatorBrain.m
// Calculator
//
// Created by Michele Cleary on 2/25/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray *programStack;
#property (nonatomic, strong) NSDictionary *testVariable;
#property (nonatomic) BOOL numberHandledNextOperation;
- (double) convertRadianToDegree: (double) radian;
#end
#implementation CalculatorBrain
#synthesize programStack = _programStack;
#synthesize testVariable = _testVariable;
#synthesize numberHandledNextOperation = _numberHandledNextOperation;
- (NSMutableArray *)programStack
{
if (_programStack == nil) _programStack = [[NSMutableArray alloc] init];
return _programStack;
}
//- (void)setOperandStack:(NSMutableArray *)operandStack
//{
// _operandStack = operandStack;
//}
- (void)pushOperand:(double)operand
{
[self.programStack addObject:[NSNumber numberWithDouble:operand]];
}
- (double)performOperation:(NSString *)operation
{
[self.programStack addObject:operation];
return[CalculatorBrain runProgram:self.program];
}
- (id)program
{
return [self.programStack copy];
}
+ (NSString *)descriptionOfProgram:(id)program
{
self.numberHandledNextOperation = NO; //1. this is a problem with xcode: member reference type struct objc_class * is a pointer; maybe you meant to use ->
NSMutableSet * displayDescrip = [[NSMutableSet alloc] init];
for(id foundItemKey in program)
{
if ([foundItemKey isKindOfClass:[NSString class]])
//operator or variable
{
if ([foundItemKey isEqualToString:#"sin"]&&(!self.numberHandledNextOperation))
{ //2. xcode says To match this {.
NSObject *nextObj = [program objectForKey:(foundItemKey+1); //3. xcode doesn't like this: arithmetic on pointer to interface id which is not a constant size in non-fragile ABI
//[displayDescrip addObject:foundItemKey];
}
else if ([foundItemKey isEqualToString:#"cos"])
{
//[displayDescrip addObject:foundItemKey];
}
else if ([foundItemKey isEqualToString:#"sqrt"])
{
//[displayDescrip addObject:foundItemKey];
}
else if ([foundItemKey isEqualToString:#"Ï€"])
{
//[displayDescrip addObject:foundItemKey];
}
else if (![CalculatorBrain isOperationName:foundItemKey])
{
//variable
//[displayDescrip addObject:foundItemkey];
}
else if (foundItemKey isKindOfClass:[NSNumber class]) //4. xcode expected ]
{
//number
//if next object is operation
if(isOperation([program objectForKey:(foundItemKey+1)))
{
numberHandledNextOperation = YES;
if(isOperationSpecial([program objectForKey:(foundItemKey+1)))
{ //sin or cos or sqrt need parentheses
//[displayDescrip addObject:(foundItemKey+1)];
//[displayDescrip addObject:#"("];
//[displayDescrip addObject:foundItemKey];
//[displayDescrip addObject:#")"];
}
else
{ //regular operation + - / *
//[displayDescrip addObject:(foundItemKey+1)];
//[displayDescrip addObject:(foundItemKey)];
}
numberHandledNextOperation = YES;
} //if
} //else if
} //if
} //for
//not sure if I need this next thing
//NSSet * returnedVarNames = [varNames copy];
//return returnedVarNames;
return #"implement this in Assignment 2";
}
+ (double)runProgram:(id)program
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self popOperandOffStack:stack];
}
+ (double)runProgram:(id)program usingVariableValues:(NSDictionary *)variableValues
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
if(variableValues)
{
int numItemsDisplayed = [stack count];
for (int count = 0; count < numItemsDisplayed; count++)
{
id foundItem = [stack objectAtIndex:count];
if ([foundItem isKindOfClass:[NSString class]])
{
NSString * var = [variableValues objectForKey:foundItem];
if(var)
{
[stack replaceObjectAtIndex:count withObject:[NSNumber numberWithDouble:[var doubleValue]]];
}
}
}
}
return [self popOperandOffStack:stack];
}
+ (double)popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack) [stack removeLastObject];
if([topOfStack isKindOfClass:[NSNumber class]]){ //number
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]]){ //string operation
NSString *operation = topOfStack;
if ([operation isEqualToString:#"+"]) {
result = [self popOperandOffStack:stack] + [self popOperandOffStack:stack];
}else if ([operation isEqualToString:#"*"]) {
result = [self popOperandOffStack:stack] * [self popOperandOffStack:stack];
}else if ([operation isEqualToString:#"/"]) {
double divisor = [self popOperandOffStack:stack];
if (divisor)
result = [self popOperandOffStack:stack] / divisor;
}else if ([operation isEqualToString:#"-"]) {
double subtrahend = [self popOperandOffStack:stack];
result = [self popOperandOffStack:stack] - subtrahend;
}else if ([operation isEqualToString:#"sin"]) {
result = result = (sin([self popOperandOffStack:stack])); //(sin([self convertRadianToDegree:[self popOperandOffStack:stack]]));
}else if ([operation isEqualToString:#"cos"]) {
result = (cos([self popOperandOffStack:stack]));
}else if ([operation isEqualToString:#"sqrt"]) {
result = (sqrt([self popOperandOffStack:stack]));
}else if ([operation isEqualToString:#"π"]) {
result = M_PI;
}else{
result = 0;
}
}
return result;
}
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet * varNames = [[NSMutableSet alloc] init];
for(id foundItem in program)
{
if ([foundItem isKindOfClass:[NSString class]])
{
if (![CalculatorBrain isOperationName:foundItem])
{
[varNames addObject:foundItem];
}
}
}
NSSet * returnedVarNames = [varNames copy];
return returnedVarNames;
}
+ (BOOL)isOperationName:(NSString *)foundItem
{
NSSet *myOperationSet = [NSSet setWithObjects:#"sqrt", #"sin", #"cos", #"π", #"+", #"-", #"*", #"/", nil];
return([myOperationSet containsObject:(foundItem)]);
}
- (NSString *)description
{
return [NSString stringWithFormat:#"stack = %#", self.programStack];
}
-(double) convertRadianToDegree: (double) radian;
{
return M_PI*2*radian/360;
}
#end //6. xcode expected }
+ (NSString *)descriptionOfProgram:(id)program
Do you actually want descriptionOfProgram a class + method ? If yes, it is more like a static method in C++. It doesn't belong to any particular instance of a class. There is no hidden parameter of constant pointer to the current instance is passed.