Calling ViewDidAppear from numerous methods to update NSMutableDictionary data entries - ios

I am fairly new to Objective C and am attempting to develop an app using Xcode5.
I am storing strings (either composed of numbers 1-9 or N/A) in a NSMutableDictionary.
When users get to the "Review your inputed results page" I want them to be able to manually go into a text field, delete the value present and retype their new value if necessary. However, I don't know how to reload this information into the system so that the new values will carry over into the email client, which basically sends the results to whatever email address the user wishes.
Currently, the values are being loaded using ViewDidAppear upon entering the UIView, but I think I need to call it again if, for example, textField1 is updated.
I have methods for all the textFields that are textField(insert correct number here)IsUpdated and inside those I want to store the new value to the NSMutableDictionary (which I believe I can already do).
The issue is I cannot figure out how to get the current version of the dictionary that was loaded upon entering the UIView to update so that the information in ViewDidAppear updates for the email.
Hope that made sense.
As I said, definitely new to Objective C.
Below is the viewDidAppear method.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:(BOOL)animated];
AppDelegate *app = (AppDelegate *) [[UIApplication sharedApplication] delegate];
NSMutableDictionary *results = [app results];
NSString *firstResult = [results valueForKey:#"first"];
NSString *secondResult = [results valueForKey:#"second"];
NSString *thirdResult = [results valueForKey:#"third"];
if ([firstResult isEqual: #"N/A"]) {
self.Result1.text = results[#"first"];
} else {
int firstResultInt = [firstResult intValue]; firstResultInt++;
[_Result1 setText:[NSString stringWithFormat:#"%i", firstResultInt]];
}
if ([secondResult isEqual: #"N/A"]) {
self.Result2.text = results[#"second"];
} else {
int secondResultInt = [secondResult intValue]; secondResultInt++;
[_Result2 setText:[NSString stringWithFormat:#"%i", secondResultInt]];
}
if ([thirdResult isEqual: #"N/A"]) {
self.Result3.text = results[#"third"];
} else {
int thirdResultInt = [thirdResult intValue]; thirdResultInt++;
[_Result3 setText:[NSString stringWithFormat:#"%i", thirdResultInt]];
}
self.diningResult.text = results[#"dining"];
self.basementResult.text = results[#"basement"];
self.atticResult.text = results[#"attic"];
self.carResult.text = results[#"car"];
self.hallwayResult.text = results[#"hallway"];
self.garageResult.text = results[#"garage"];
self.other1Result.text = results[#"other"];
self.other2Result.text = results[#"other1"];
self.other1Name.text = results[#"other1name"];
self.other2Name.text = results[#"other2name"];
NSMutableString * str = [NSMutableString new];
[str appendString:#"Bedroom: "];
if ([firstResult isEqual: #"N/A"]) {
[str appendString: firstResult];
} else {
int firstResultInt = [firstResult intValue]; firstResultInt++;
NSString *firstResultString = [NSString stringWithFormat:#"%d",firstResultInt];
[str appendString: firstResultString];
}
[str appendString:#"\n"];
[str appendString:#"Living Room: "];
if ([secondResult isEqual: #"N/A"]) {
[str appendString: secondResult];
} else {
int secondResultInt = [secondResult intValue]; secondResultInt++;
NSString *secondResultString = [NSString stringWithFormat:#"%d",secondResultInt];
[str appendString: secondResultString];
}
[str appendString:#"\n"];
[str appendString:#"Kitchen: "];
if ([thirdResult isEqual: #"N/A"]) {
[str appendString: thirdResult];
} else {
int thirdResultInt = [thirdResult intValue]; thirdResultInt++;
NSString *thirdResultString = [NSString stringWithFormat:#"%d",thirdResultInt];
[str appendString: thirdResultString];
}
[str appendString:#"\n"];
[str appendString:#"Dining:"];
[str appendString:self.diningResult.text];
[str appendString:#"\n"];
//Code goes on to do the same with all other fields. all strings led by "str" get transferred over to the email
self.emailString = [NSString stringWithString:str];
}

The code should not call viewDidAppear, it's the responsibility of the framework to call viewDidAppear at the appropriate times. Instead, you should make a separate methods, e.g. UpdateMailContents and UpdateTextFields. Then call those methods from viewDidAppear, and call UpdateMailContents from the textFieldDidEndEditing method of the UITextFieldDelegate protocol.

Related

Delete Button Calculator *Help*

I have this code for my calculator that lets me delete one number at a time!
- (IBAction)deleteButton:(id)sender {
NSString *string = [Screen text];
int length = [string length];
NSString *temp = [string substringToIndex:length-1];
if ([temp length] == 0) {
temp = #"0";
}
[Screen setText:temp];
}
It works, but whenever I enter another number, it resets back to the whole thing, so lets take this as an example.
I have the number 5678, (I deleted 678), So my new number is 5, (Now if I press another number), it goes back to 56781 ( 1 being the new number)
Heres my full code for my project! ---> http://txt.do/oduh
As seen in your code, you are using SelectNumber to store value everywhere, but in deleteButton method you are not storing new value in SelectNumber. So you need to set the new value in deleteButton.
- (IBAction)deleteButton:(id)sender {
NSString *string = [Screen text];
int length = [string length];
NSString *temp = [string substringToIndex:length-1];
if ([temp length] == 0) {
temp = #"0";
}
SelectNumber = [temp intValue]; // set new value here
[Screen setText:temp];
}

Split NSString from first whitespace

I have a name textfield in my app, where both the firstname maybe a middle and a lastname is written. Now I want to split these components by the first whitespace, the space between the firstname and the middlename/lastname, so I can put it into my model.
For example:
Textfield Text: John D. Sowers
String 1: John
String 2: D. Sowers.
I have tried using [[self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] firstObject]; & [[self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lastObject];
But these only work if have a name without a middlename. Since it gets the first and the last object, and the middlename is ignored.
So how would I manage to accomplish what I want?
/*fullNameString is an NSString*/
NSRange rangeOfSpace = [fullNameString rangeOfString:#" "];
NSString *first = rangeOfSpace.location == NSNotFound ? fullNameString : [fullNameString substringToIndex:rangeOfSpace.location];
NSString *last = rangeOfSpace.location == NSNotFound ? nil :[fullNameString substringFromIndex:rangeOfSpace.location + 1];
...the conditional assignment (rangeOfSpace.location == NSNotFound ? <<default value>> : <<real first/last name>>) protects against an index out of bounds error.
Well that method is giving you an array with all the words split by white space, so then you can grab the first object as the first name and the rest of the objects as middle/last/etc
NSArray *ar = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *firstName = [ar firstObject];
NSMutableString *rest = [[NSMutableString alloc] init];
for(int i = 1; i < ar.count; i++)
{
[rest appendString:[ar objectAtIndex:i]];
[rest appendString:#" "];
}
//now first name has the first name
//rest has the rest
There might be easier way to do this, but this is one way..
Hope it helps
Daniel
I think this example below I did, solves your problem.
Remember you can assign values from the array directly, without transforming into string.
Here is an example:
NSString *textField = #"John D. Sowers";
NSArray *fullName = [textField componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" "]];
if (fullName.count)
{
if (fullName.count > 2)
{
NSLog(#"Array has more than 2 objects");
NSString *name = fullName[0];
NSLog(#"Name:%#",name);
NSString *middleName = fullName[1];
NSLog(#"Middle Name:%#",middleName);
NSString *lastName = fullName[2];
NSLog(#"Last Name:%#",lastName);
}
else if(fullName.count == 2)
{
NSLog(#"Array has 2 objects");
NSString *name = fullName[0];
NSLog(#"Name:%#",name);
NSString *lastName = fullName[1];
NSLog(#"Last Name:%#",lastName);
}
else
{
NSString *name = fullName[0];
}
}
I found this to be most robust:
NSString *fullNameString = #"\n Barnaby Marmaduke \n \n Aloysius ";
NSMutableArray *nameArray = [[fullNameString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
[nameArray removeObject:#""];
NSString *firstName = [nameArray firstObject];
if(nameArray.count)
{
[nameArray removeObjectAtIndex:0];
}
NSString *nameRemainder = [nameArray componentsJoinedByString:#" "];
Bob's your uncle.

Newbie trying to construct an URL for GET request - my test code and screenshot included

Being an iOS and Objective-C newbie I'm trying to construct an app, where the user can authenticate through Facebook, Google+ or 3 further (Russian) social networks.
For Facebook I know, that I could use the Facebook SDK or Social.framework, but for the others - I have to use OAuth and UIWebView, because there are no good SDKs for them yet.
(I have already succeeded in that once - but that was for an Adobe AIR app and now I'm trying to learn native...)
In Xcode 5.2 I have prepared a very simple Master-Detail app for iPhone and checked it into GitHub:
My question is about constructing a NSString for a GET (or body in POST) request -
Currently I have the following awkward source code in the DetailViewController.m:
- (NSString*)buildUrl
{
NSString *key = _dict[kKey];
NSString *str = _dict[kAuthUrl];
if ([key isEqual: kFB]) {
str = [str stringByAppendingString:#"display=touch"];
str = [str stringByAppendingString:#"&response_time=token"];
str = [str stringByAppendingString:#"&client_id="];
str = [str stringByAppendingString:_dict[kAppId]];
str = [str stringByAppendingString:#"&redirect_uri="];
str = [str stringByAppendingString:_dict[kAppUrl]];
//str = [str stringByAppendingString:#"&state="];
//str = [str stringByAppendingString:rand(1000)];
} else if ([key isEqual: kGG]) {
} else if ([key isEqual: kMR]) {
} else if ([key isEqual: kOK]) {
} else if ([key isEqual: kVK]) {
}
return str;
}
My questions:
Instead of using stringByAppendingString could I use something nicer? Like maybe an NSArray (or even better NSDictionary) and then somehow joining it with ampersands inbetween?
How to escape the HTML entities in the value of redirect_uri= ?
I need to append a random number as the value of state=, but I am not sure what function to use there best...
Here is what my app prints at the moment, for the above code:
MyAuth[9626:70b] request: { URL: https://graph.facebook.com/oauth/authorize?display=touch&response_time=token&client_id=432298283565593&redirect_uri=https://www.facebook.com/connect/login_success.html }
(which is no good: the URL at the end is not escaped and there is no random state number).
stringWithFormat: is your friend.
- (NSString*)buildUrl
{
NSString *key = _dict[kKey];
NSString *str = _dict[kAuthUrl];
if ([key isEqual: kFB]) {
NSString *escapedURI = [_dict[kAppUrl] stringByAddingPercentEscapesUsingEncoding:NSUTF8Encoding];
int state = arc4random_uniform(1000);
str = [NSString stringWithFormat:#"%#display=touch&response_time=token&client_id=%#&redirect_uri=%#&state=%d", _dict[kAuthUrl], _dict[kAppId], escapedURI, state];
} else if ([key isEqual: kGG]) {
} else if ([key isEqual: kMR]) {
} else if ([key isEqual: kOK]) {
} else if ([key isEqual: kVK]) {
}
return str;
}

iOS Memory Management & NSString Initialisation

Still learning iOS development with ObjectiveC and iOS, and trying to realy understand memory management! Appreciate any advise on the snippet below, eg:
1) Analyser says there are potential memory leaks, but can't solve them?
2) Should I keep alloc and init the NSStrings in the for loop and when appended to?
Thanks
- (NSString *) lookUpCharNameForID: (NSString *) inCharID
{
debugPrint ("TRACE", [[#"Lookup Char Name for = " stringByAppendingString: inCharID] UTF8String]);
NSString *tempName = [[NSString alloc] initWithFormat: #""];
if (![inCharID isEqualToString: #""])
{
// Potentially lookup multiple values
//
NSString *newName = [[NSString alloc] initWithFormat: #""];
NSArray *idList = [inCharID componentsSeparatedByString: #","];
for (NSString *nextID in idList)
{
NSLog( #"Lookup %i : %#", [idList count], nextID);
newName = [[NSString alloc] initWithFormat: #"C%#", nextID];
// Append strings
if ([tempName isEqualToString: #""])
tempName = [[NSString alloc] initWithFormat: #"%#", newName];
else
tempName = [[NSString alloc] initWithFormat: #"%#+%#", tempName, newName];
}
[newName release];
}
return [tempName autorelease];
}
You don't need any of the calls to alloc, release, or autorelease. Instead, use [NSString stringWithFormat:] to create instances of NSString that you don't own, and therefore don't need to manage. Also, consider using NSMutableString to simplify your code a bit, for example along the lines of the following (untested) version:
- (NSString *) lookUpCharNameForID: (NSString *) inCharID
{
NSMutableString *tempName = nil;
if (![inCharID isEqualToString: #""])
{
NSArray *idList = [inCharID componentsSeparatedByString: #","];
for (NSString *nextID in idList)
{
[tempName appendString:#"+"]; // Does nothing if tempName is nil.
if (tempName == nil)
tempName = [NSMutableString string];
[tempName appendFormat:#"C%#", nextID];
}
}
return tempName;
}
You have 2 alloc initWithFormat for tempName. One before the loop and one within the loop.
Use ARC (Automatic Reference Counting) for new projects. For older projects it may be easy to convert them, if not ARC can be disabled on a file-by-file basis where necessary.
Using a mutable string, autoreleased convience methods and a little rerfactoring:
- (NSString *) lookUpCharNameForID: (NSString *) inCharID
{
NSMutableString *tempName = [NSMutableArray array];
if (inCharID.length)
{
NSArray *idList = [inCharID componentsSeparatedByString: #","];
for (NSString *nextID in idList)
{
if (tempName.length == 0)
[tempName appendFormat: #"%#C", nextID];
else
[tempName appendFormat: #"+%#C", nextID];
}
}
return tempName;
}

How to check if an NSString contains one of the NSStrings in an NSArray?

I'm making an iOS app and I need to figure out if an NSString contains any of the NSStrings in an NSArray.
BOOL found=NO;
for (NSString *s in arrayOfStrings)
{
if ([stringToSearchWithin rangeOfString:s].location != NSNotFound) {
found = YES;
break;
}
}
It may be a silly optimization for your use case, but depending on how large the array is that you are iterating, it may be helpful/more performant to use NSArray's indexOfObjectWithOptions:passingTest: method.
With this method you pass some options and a block that contains your test. Passing the NSEnumerationConcurrent option will allow the evaluation of your block to occur on multiple threads concurrently and potentially speed things up. I reused invariant's test, but in a slightly different way. The block functionally returns a BOOL similar to the "found" variable in invariant's implementation. The "*stop = YES;" line indicates that iterating should stop.
See the NSArray reference documentation for more info. Reference
NSArray *arrayOfStrings = ...;
NSString *stringToSearchWithin = #"...";
NSUInteger index = [arrayOfStrings indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^(id obj, NSUInteger idx, BOOL *stop)
{
NSString *s = (NSString *)obj;
if ([stringToSearchWithin rangeOfString:s].location != NSNotFound) {
*stop = YES;
return YES;
}
return NO;
}];
if (arrayOfStrings == nil || index == NSNotFound)
{
NSLog(#"The string does not contain any of the strings from the arrayOfStrings");
return;
}
NSLog(#"The string contains '%#' from the arrayOfStrings", [arrayOfStrings objectAtIndex:index]);
Very small security improvement on Adam's answer: there is a big issue with "objectAtIndex:" because it is totally not thread-safe and will make your app crash much too often. So I do:
NSArray *arrayOfStrings = ...;
NSString *stringToSearchWithin = ...";
__block NSString *result = nil;
[arrayOfStrings indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^(NSString *obj, NSUInteger idx, BOOL *stop)
{
if ([stringToSearchWithin rangeOfString:obj].location != NSNotFound)
{
result = obj;
*stop = YES;
//return YES;
}
return NO;
}];
if (!result)
NSLog(#"The string does not contain any of the strings from the arrayOfStrings");
else
NSLog(#"The string contains '%#' from the arrayOfStrings", result);
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %#", theArray];
BOOL result = [predicate evaluateWithObject:theString];
As with the release of iOS8, Apple added a new method to NSStringcalled localizedCaseInsensitiveContainsString. This will exactly do what you want way easier:
BOOL found = NO;
NSString *string = #"ToSearchFor";
for (NSString *s in arrayOfStrings){
if ([string localizedCaseInsensitiveContainsString:s]) {
found = YES;
break;
}
}

Resources