Key/Value update string value - iphone

The problem I get when I use the codes below is that the totalScore only updates when the viewDidLoad method in the secondViewController is called. So if I press the addOne button twice in a row the retrievingString is the same both times so it will just add one to the same existing value, because it haven't been updated in the secondViewController. How can I manage to update the totalScore in secondViewController each time the addOne button is pressed in firstViewController
firstViewController:
-(void)saveString:(NSString *)myString {
[[NSUserDefaults standardUserDefaults] setObject:myString forKey:#"firstScore"];
}
-(NSString *)retrieveString {
NSString *recoveredString = [[NSUserDefaults standardUserDefaults]objectForKey:#"totalScore"];
return recoveredString;
}
-(IBAction) addOne {
NSString *totalScore = [self retrieveString];
NSInteger totalScoreInt = [totalScore intValue];
NSInteger newScore = totalScoreInt+1;
NSString *newScoreString = [NSString stringWithFormat:#"%i", newScore];
[self saveString:newScoreString];
}
secondViewController:
-(void)saveString:(NSString *)myString {
[[NSUserDefaults standardUserDefaults] setObject:myString forKey:#"totalScore"];
}
-(NSString *)retrieveString {
NSString *recoveredString = [[NSUserDefaults standardUserDefaults]objectForKey:#"firstScore"];
return recoveredString;
}
-(void) viewDidLoad {
NSString *firstScore = [self retrieveString];
NSInteger firstScoreInt = [firstScore intValue];
NSInteger newTotalScore = firstScoreInt+1;
NSString *newTotalScoreString = [NSString stringWithFormat:#"%i", newTotalScore];
[self saveString:newTotalScoreString];
}
I hope I have explained the problem understandable, so maybe someone can help me with this.

If I'm understanding this correctly, the error that u'r making here is that since addOne function in your firstViewController is also updating the total score, but you are not saving the total score there. So in your saveString function in the firstViewController, add this line too :
[[NSUserDefaults standardUserDefaults] setObject:myString forKey:#"totalScore"];
Hope this helps

You are working with 2 different scores, taking the value from firstScore, increasing it and storing it into totalScore. Check what you are trying to store in each.
Here's an example of why your code fails:
firstScore is 1
totalScore is whatever
then you addOne, which takes firstScore (1), adds one to it (2), and stores in totalScore, which leaves it like this:
firstScore is 1
totalScore is 2
you addOne again, and it reads firstScore (1), adds one (2) and stores again, so nothing really changed.

Related

UIProgressView indicator not updating with the correct "progress"

I am downloading images asynchronously and displaying them in a UITableView. While theimage is downloading, a UIProgressView should be displayed in the corresponding table row. After the download is complete, progress view should be replaced by the actual image.
In my table view, I am using a custom cell called ProgressTableViewCell subclassed from UITableViewCell. It has a UIProgressView IBOutlet.
I have created an NSOperation from NSURLConnection and added them to an NSOperationQueue. As the delegate's
didReceiveData
method is called, a notification is posted to my table view controller to update the corresponding table row with
reloadRowsAtIndexPaths
method of table view. My cellForRowAtIndexPath does the following for the reloaded row:
ProgressTableViewCell *cell = (ProgressTableViewCell*)[tableView dequeueReusableCellWithIdentifier:#"ProgressCell"];
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
NSNumber* percentage= [NSNumber numberWithFloat:received/total];
NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
NSLog(#"percentage %f", percentage.floatValue);
[userInfo setObject:cell forKey:#"cell"];
[userInfo setObject:percentage forKey:#"percentage"];
[self performSelectorOnMainThread:#selector(updateProgressView:) withObject:userInfo waitUntilDone:NO];
NSLog(#"received: %#", [downloadInfo objectForKey:#"receivedBytes"]);
NSLog(#"Progress: %f", cell.progressView.progress);
return cell;
The updateProgressView method looks like
- (void)updateProgressView :(NSMutableDictionary *)userInfo
{
ProgressTableViewCell* cell = [userInfo valueForKey:#"cell"];
NSNumber* progress = [userInfo valueForKey:#"percentage"];
[cell.progressView setProgress:progress.floatValue ];
NSLog(#"Progress after update: %f", cell.progressView.progress);
}
I am updating the progress view on the main thread and I have even tried setting waitUntilDone to YES but to no avail. My progress view stays at the zero point. Occasionally when I am debugging I can see some change in the progress indicator which makes me think it might be a timing problem. But how to solve it?
EDIT: Here is NSURLConnection delegate's didReceiveData method:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_responseData appendData:data];
NSNumber* bytes = [NSNumber numberWithUnsignedInt:[data length]];
NSLog(#"received bytes:%d", [bytes intValue] );
NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
[userInfo setObject:_responseId forKey:#"responseId"];
[userInfo setObject:bytes forKey:#"receivedBytes"];
[self fireNotification: [NSNotification
notificationWithName:#"DidReceiveData"
object:self userInfo:userInfo]];
}
- (void)fireNotification :(NSNotification *)aNotification
{
[[NSNotificationCenter defaultCenter] postNotification:aNotification];
}
And here is my view controller's method that gets the notification:
-(void) dataReceived:(NSNotification *)notification {
NSNumber* responseId = [[notification userInfo] objectForKey:#"responseId"];
NSNumber* bytes = [[notification userInfo] objectForKey:#"receivedBytes"];
NSMutableDictionary* downloadInfo = [self getConnectionInfoForId:responseId];
NSLog(#"received bytes:%ld for response %#", [bytes longValue], responseId );
NSNumber* totalBytes = [NSNumber numberWithInt:([bytes longValue] + [[downloadInfo objectForKey:#"receivedBytes"] longValue]) ];
[downloadInfo setObject:totalBytes forKey:#"receivedBytes"];
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
[downloadInfo setObject:[NSNumber numberWithFloat:received/total] forKey:#"progress"];
[self reloadRowForResponse:responseId];
}
I have also added a nil check to my cellForRowAtIndexpath method as recommended:
ProgressTableViewCell *cell = (ProgressTableViewCell*)[tableView dequeueReusableCellWithIdentifier:#"ProgressCell"];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ProgressCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
NSNumber* percentage= [NSNumber numberWithFloat:received/total];
NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
NSLog(#"cell:%#", cell);
NSLog(#"percentage %f", percentage.floatValue);
[userInfo setObject:cell forKey:#"cell"];
[userInfo setObject:percentage forKey:#"percentage"];
[self performSelectorOnMainThread:#selector(updateProgressView:) withObject:userInfo waitUntilDone:NO];
return cell;
I think you're taking the wrong approach by reloading the table cell every time the delegate method gets called. You can instead just grab the visible cell and update the progress indicator directly, rather than going through the data source.
I'm assuming you have some way of converting responseId to the index path of the row you want to update -- let's say that's called indexPathForResponseID: in your controller. Rather than reloading the cell, you can just grab the cell if it's visible and update its progress indicator:
- (void)dataReceived:(NSNotification *)notification {
...
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
NSIndexPath *cellPath = [self indexPathForResponseID:responseId];
ProgressTableViewCell *cell = (ProgressTableviewCell *)[self.tableView cellForRowAtIndexPath:cellPath];
if (cell) {
// make sure you're on the main thread to update UI
dispatch_async(dispatch_get_main_queue(), ^{
[cell.progressView setProgress: (received / total)];
}
}
}
You should be aware, though, that this solution won't suffice if you have more downloads than visible cells -- you should also be storing the progress of each download somewhere in your data source so that if the table view DOES need to reload a cell (due to a scroll), it knows how to set the progress indicator.
I've found in many cases like this that the cell you think you have isn't the one that's updating. When you reload, your code is popping a cell off the reusable, which is basically an old cell. If you reload, that cell gets replaced by another. (You also haven't included allocating a new cell if the reusable ones return nil) If your cell scrolls off, it gets reloaded, so you have to make sure that you're putting the progress bar on the cell that's there, not the one that used to be there. You might want to NSLog the address of the cell you started the transfer with, and then again each time you update the progress. You need to update the progress bar with the current cell at that index path. It may not be the one you initially started the process with.
Coming back to this almost 2 months later, I seem to find a solution. Not sure if this is good practice but creating a new UIProgressView each time the progress is updated seems to solve my problem. Here is the method:
-(void)updateProgressView :(NSMutableDictionary *)userInfo
{
ProgressTableViewCell* cell = [userInfo objectForKey:#"cell"];
cell.backgroundColor =[UIColor darkGrayColor];
NSNumber* progress = [userInfo objectForKey:#"percentage"];
NSLog(#"Progress before update: %f", cell.progressView.progress);
UIProgressView *pView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
pView.frame = CGRectMake(150, 200, 150, 9);
pView.progress = progress.floatValue;
[cell.contentView addSubview:pView];
}
Many thanks to everyone for their help.

Why can't a Double value stored as Object in NSUserDefault is not reflected in UITableViewCell?

I am trying to store Double Value in NSUserDefault But though i am able to store it (as my NSLog value shows true value), when I tried to reload UITableView, its Cell value is not updated with current value in userdefault.
This weird behavior happens only when i call my setUserDefaults method from delegate method of UIAlertView
Why such weird behavior happens??
Here is my code:
- (void)setUserDefaults
{
NSLog(#"setUserDefault : empArray: %d, empCount: %d",empArray.count, empCount);
NSMutableDictionary *empData = [[NSMutableDictionary alloc] init];
if (empArray.count>1)
empData = [empArray objectAtIndex:(empCount-1)]; // because empCount starts from 1. and empArray[0] = empCount 1
else
empData = [empArray objectAtIndex:0];
[userDefault setObject:[NSString stringWithFormat:#"%.2f",[empData objectForKey:#"salary"]] forKey:#"salary"];
NSLog(#"setUserDefaults: salary=%.2f",[[empData objectForKey:#"salary"] doubleValue]);
[empData release];
[self.tblView reloadData];
}
Delegate method of UIAlertView goes as below:
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger) buttonIndex
{
if (alertView.tag == 1) // btnRemoveEmpPressed.
{
if(buttonIndex==0)
{
NSLog(#"buttonIndex 0");
}
else
{
NSLog(#"empRemovedPressed OK, buttonIndex 1, empArray Count %d, empCount %d",empArray.count, empCount);
// [empArray removeObjectAtIndex:(empCount)];
empCount -= 1;
lblTitle.text = [NSString stringWithFormat:#"Employee %d",empCount];
[self setUserDefaults];
}
// [tblView reloadData];
}
else if (alertView.tag == 2)
{
if (buttonIndex==1)
{
[self resetUserDefaults];
empCount = 1;
[empArray removeAllObjects];
lblTitle.text = [NSString stringWithFormat:#"Employee %d",empCount];
}
}
}
You should use the setDouble:forKey: method of NSUserDefaults and let it manage the value for you. Also synchronize the NSUserDefaults in order to save the value for later usage.
You aren't using doubleValue when setting user defaults. Either do that or, as herz says, use the setDouble method.
You aren't calling synchronize on your defaults object after updating it
Don't release empData, you didn't retain it
I'm assuming userDefault is set up elsewhere and it is not nil when you are using it?

Spotted a leak in UITextView delegate method. Confused about solution

I've got a problem with an UITextView and one of its delegate methods in my navigation based app:
- (BOOL)textView:aView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
I managed to limit the max length of text the user can input using the above method. But I'm using a leaky array for that matter I think.
The problem is:
I want to save the amount of typed characters right in the very moment the user enters the last line of my textview. I then use that value to calculate the string length - which I compare to the textview's content size to set a limit. The code works fine - but since the method it's inside of is updating with every text input, I'm having trouble releasing the array in the right moment.
Here's some code:
if (numLines == 9)
{
if (!numCharsArray)
{
numCharsArray = [[NSMutableArray alloc] initWithCapacity:1]; // Stack trace gives this line 3,3% of the leak.
}
numChars = tView.text.length;
NSNumber *number = [[NSNumber alloc] initWithInteger:numChars]; // This line gets 77,3%.
[numCharsArray addObject:number]; // This line gets the rest, 24,3%.
[number release];
startChars = [[numCharsArray objectAtIndex:0] integerValue];
NSString *lastLine = [[NSString alloc]initWithString:[[tView text] substringFromIndex:startChars]];
CGSize lineSize = [lastLine sizeWithFont:tView.font forWidth:tView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
[lastLine release];
if (range.length > text.length)
{
return YES;
}
else if (numLines == 9 && lineSize.width >= tView.contentSize.width - 45)
{
return NO;
}
}
else
{
numCharsArray = nil;
/*
if(numCharsArray)
{
[numCharsArray release];
}
*/
}
I tried the out-commented statement above, but that gives me an app crash once I leave the last line of the textview. And as you can see in the code comments - without releasing the array I get a leak.
So how and where do I release that array correctly - keeping it safe while the user is on the last line?
Just replace with
first one
numCharsArray = [NSMutableArray array]; // you do not need to release
//explicitly as its autorelease numberWithInt
second one
NSNumber *number = [NSNumber numberWithInt:numChars]; //autorelease
NSString *lastLine = [[tView text] substringFromIndex:startChars];

Can't read NSUserDefaults data between views

Disclaimer: major noob
I'm writing an arithmetic flash card app as a learning project. I have a UITabViewController with the bottom tab bar that toggles between a few different views. Everything works okay until I try to set NSUserDefault boolean values in the Settings view controller and try to read those values in the Flashcards view controller.
The settings view has a switch to enable/disable each operator (addition, subtraction, etc) and the flashcard view should randomly present a flash card if that type of operation was enabled.
I believe that my mistake is that I don't understand the key concept of data encapsulation, objects, etc. I'd appreciate any help.
Here is the Settings view controller. I'm not even sure how to put the code into this forum so this might be a laughable moment... here goes:
// settings_flashcards.h
#import <UIKit/UIKit.h>
#interface settings_flashcards : UIViewController {
UISwitch *additionSwitch;
UISwitch *subtractionSwitch;
UISwitch *multiplicationSwitch;
UISwitch *divisionSwitch;
}
#property (nonatomic,retain) IBOutlet UISwitch *additionSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *subtractionSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *multiplicationSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *divisionSwitch;
#end
and...
/ settings_flashcards.m
#import "settings_flashcards.h"
#implementation settings_flashcards
#synthesize additionSwitch;
#synthesize subtractionSwitch;
#synthesize multiplicationSwitch;
#synthesize divisionSwitch;
- (void)viewDidLoad {
[additionSwitch addTarget:self action:#selector(additionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[subtractionSwitch addTarget:self action:#selector(subtractionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[multiplicationSwitch addTarget:self action:#selector(multiplicationSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[divisionSwitch addTarget:self action:#selector(divisionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[super viewDidLoad];
}
-(void) additionSwitchFlipped {
if (additionSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"additionKey"];
}
}
-(void) subtractionSwitchFlipped {
if (subtractionSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"subtractionKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"subtractionKey"];
}
}
-(void) multiplicationSwitchFlipped {
if (multiplicationSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"multiplicationKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"multiplicationKey"];
}
}
-(void) divisionSwitchFlipped {
if (divisionSwitch.on) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"divisionKey"];
}else {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"divisionKey"];
}
}
Here is the Flashcards view...
// flashcardsViewController.h
#import <UIKit/UIKit.h>
#interface flashcardsViewController : UIViewController <UIActionSheetDelegate>{
UILabel *firstNumberLabel;
UILabel *secondNumberLabel;
UILabel *answerNumberLabel;
UILabel *operatorLabel;
BOOL additionIsEnabled;
BOOL subtractionIsEnabled;
BOOL multiplicationIsEnabled;
BOOL divisionIsEnabled;
}
#property (nonatomic,retain) IBOutlet UILabel *firstNumberLabel;
#property (nonatomic,retain) IBOutlet UILabel *secondNumberLabel;
#property (nonatomic,retain) IBOutlet UILabel *answerNumberLabel;
#property (nonatomic,retain) IBOutlet UILabel *operatorLabel;
-(void) buttonClicked:(id)sender;
#end
and...
// flashcardsViewController.m
#import "flashcardsViewController.h"
#implementation flashcardsViewController
#synthesize firstNumberLabel;
#synthesize secondNumberLabel;
#synthesize answerNumberLabel;
#synthesize operatorLabel;
- (void)viewDidLoad {
srand(time(0)); //seed random
//the following should assign the keys if they don't exist
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"additionKey"]){
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"subtractionKey"]) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"subtractionKey"];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"multiplicationKey"]) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"multiplicationKey"];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"divisionKey"]) {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"divisionKey"];
}
//the following should assign each BOOL variable based on the key
additionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"additionKey"];
subtractionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"subtractionKey"];
multiplicationIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"multiplicationKey"];
divisionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"divisionKey"];
[super viewDidLoad];
}
-(void) buttonClicked:(id)sender{
int a = rand() % 4;// random number generator (number to enter loop)
if ( additionIsEnabled || subtractionIsEnabled || multiplicationIsEnabled || divisionIsEnabled) {
while (a < 5) {
switch (a) {
case 0:
if (additionIsEnabled == TRUE){
int x = rand() % 11 + 1;
int y = rand() %11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x+y)];
operatorLabel.text = [[NSString alloc]initWithFormat: #"+"];
a = 5;
}
else a++;
break;
case 1:
if (subtractionIsEnabled == TRUE){
int x = rand() % 19 + 1;
int y = rand() %11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x-y) ];
operatorLabel.text = [[NSString alloc]initWithFormat: #"-"];
a = 5;
}
else a++;
break;
case 2:
if (multiplicationIsEnabled == TRUE){
int x = rand() % 11 + 1;
int y = rand() %11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x*y)];
operatorLabel.text = [[NSString alloc]initWithFormat: #"×"];
a = 5;
}
else a++;
break;
case 3:
if (divisionIsEnabled == TRUE){
int x = rand() % 11 + 1;
int y = rand() % 11 + 1;
firstNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",(x*y)];
secondNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",y];
answerNumberLabel.text = [[NSString alloc]initWithFormat: #"%i",x];
operatorLabel.text = [[NSString alloc]initWithFormat: #"÷"];
a = 5;
}
else a = 0;
break;
default:
break;
}
}
}
else
{
UIAlertView *noOperatorSelectedAlert = [[UIAlertView alloc]initWithTitle:#"You have not set any operations."
message:#"Return to the settings menu and decide which operations you wish to perform. (addition, subtraction, etc.)"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[noOperatorSelectedAlert show];
[noOperatorSelectedAlert release];
}
}
There are a few things to do here. Firstly you want a better way of saying what the default state is before the user has made any explicit decisions. Next you want to know when you should refresh your in-app state from user preferences.
Defaults Initialization
The first item's solution is to put the default values into the registration domain for your user preferences. This is something you'll do during application initialization, rather than having your views individually check preferences and update them at initialization time. The preferences system looks in quite a few places for data, and the first place it looks is in the in-memory-only registration domain. This is where you'll put the default (i.e. no value specified means 'x') values for each of your user preferences.
The API you'll use for this is -[NSUserDefaults registerDefaults:], which takes an NSDictionary of values. To set your default values of YES (in Objective-C the BOOL type uses YES and NO rather than TRUE and FALSE) you'll use something like this, commonly executed in a +initialize method for your application's main class:
+ (void) initialize
{
// in any +initialize, make sure it's being called on your class
// +initialize is different from all other methods in this respect
if ( [self isKindOfClass: [MyApplicationDelegate class]] == NO )
return; // being called on a superclass, don't do my stuff
// set up default values for certain preferences
NSMutableDictionary * defaults = [NSMutableDictionary new];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"additionKey"];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"subtractionKey"];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"multiplicationKey"];
[defaults setObject: [NSNumber numberWithBool: YES] forKey: #"divisionKey"];
// set this as the registration domain
[[NSUserDefaults standardUserDefaults] registerDefaults: defaults];
[defaults release];
}
Usually you'll put everything into one method like this, so if you have other parts of the application which expect a default non-zero value for any preference, you should add those to this group and place it into your application delegate's +initialize method.
Now, when your other classes use [[NSUserDefault standardUserDefaults] boolForKey: #"additionKey"] and there is nothing saved to the preference files for that key, they will get the value supplied above.
Refreshing Cached Values
So, you have a view where the user is able to change these preferences. Your next job is to make the view above that update its member variables using the new preferences. For this we can use either delegation or notification. In this case, I'll go with notifications for to reasons:
You're using NSUserDefaults, which can theoretically change in many different places. Delegation would only inform you of changes made by one object.
NSUserDefaults already implements a handy notification which you can watch.
So, in your flashcardsViewController you'll have something like these few methods:
- (void) updateFromPreferences
{
// fetch current values from user defaults into your member variables
additionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey: #"additionKey"];
// etc...
}
- (void) viewWillLoad
{
// load variables from user defaults
[self updateFromPreferences];
// find out when the preferences have been changed
// this will cause -updateFromPreferences to be called
// whenever something changes preferences, inside the app or outside
[[NSNotificationCenter defaultNotificationCenter] addObserver: self
selector: #selector(updateFromPreferences)
name: NSUserDefaultsDidChangeNotification
object: nil];
}
- (void) viewDidUnload
{
// always remove notification observers.
[[NSNotificationCenter defaultNotificationCenter] removeObserver: self
name: NSUserDefaultsDidChangeNotification
object: nil];
}
- (void) dealloc
{
// add this to your existing dealloc routine
[[NSNotificationCenter defaultNotificationCenter] removeObserver: self
name: NSUserDefaultsDidChangeNotification
object: nil];
}
Summary
Taken together, these two should give you everything you need to make this work.
In addition to the "always YES" problem pointed out by Chiefly Izzy, your -buttonClicked: method does not read new values from NSUserDefaults. These values are read (once) in [flashcardsViewController viewDidLoad]. If they are changed in settings, the change will not be detected in flashCardsViewController until the next time it is loaded (probably the next time the application is launched).
The simplest approach would be to move your code for reading the IsEnabled BOOL values into the -buttonClicked method, like so:
-(void) buttonClicked:(id)sender {
// the following should assign each BOOL variable based on the key
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
additionIsEnabled = [defaults boolForKey:#"additionKey"];
subtractionIsEnabled = [defaults boolForKey:#"subtractionKey"];
multiplicationIsEnabled = [defaults boolForKey:#"multiplicationKey"];
divisionIsEnabled = [defaults boolForKey:#"divisionKey"];
int a = rand() % 4;
if ( additionIsEnabled || subtractionIsEnabled ...
This code ...
//the following should assign the keys if they don't exist
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"additionKey"]){
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}
... simply always set's additionKey to TRUE. If you would like to check if additionKey is set, do this ...
//the following should assign the keys if they don't exist
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"additionKey"]){
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"additionKey"];
}
... boolForKey: documentation: If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, NO is returned.
Translated to human language - if there's an value associated with additionKey, this value is returned. If there's no associated value, NO/FALSE is returned.
So, your code does this - if value is not associated with additionKey or if it is set to NO, set it to YES. This leads to this - additionKey is always set to YES/TRUE.

Object in Array is not recognised as having "methods"

I got my application working Awhile back, but after completely and accidentally deleting it, I have tried to create it from square one. unfortunately my rewritten program is a bit cranky; does anyone see or know the possible sources of error? Also, my if statements are acting up.
-(void)loadAnnotations
{
CLLocationCoordinate2D workingCoordinate;
iProspectLiteAppDelegate *appDelegate = (iProspectLiteAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *mines =[[NSMutableArray alloc] initWithArray:(NSMutableArray *) appDelegate.mines];
BOOL gold = [[NSUserDefaults standardUserDefaults] boolForKey:#"goldControl"];
BOOL silver = [[NSUserDefaults standardUserDefaults] boolForKey:#"silverControl"];
BOOL copper = [[NSUserDefaults standardUserDefaults] boolForKey:#"copperControl"];
for(id mine in mines)
{
NSLog(#"in the loop");
workingCoordinate.latitude = [[mine latitudeInitial] doubleValue];
workingCoordinate.longitude = [[mine longitudeInitial] doubleValue];
iProspectLiteAnnotation *tempMine = [[iProspectLiteAnnotation alloc] initWithCoordinate:workingCoordinate];
[tempMine setTite:[mine mineName]];
if ([[mine commodity] isEqualToString:#"Gold"] && [gold == YES])
{
[tempMine setAnnotationType:iProspectLiteAnnotationTypeGold];
[mapView addAnnotation:tempMine];
}
if([[mine commodity] isEqualToString:#"Silver"] && [silver == YES])
{
[tempMine setAnnotationType:iProspectLiteAnnotationTypeSilver];
}
if([[mine commodity] isEqualToString:#"Copper"] && [copper == YES])
{
[tempMine setAnnotationType:iProspectLiteAnnotationTypeCopper];
}
}
[mines dealloc];
}
where the workingCoordinate.latitude = [[mine latitudeInitial] doubleValue], as well as the longitude, and [mine mineName],it says " No '-latitudeInitiallongitudeInitial' method found" or the mineName/LongitudeInitial.
also, it complains about : before ] at all the if statement lines. I don't see any errors, do you?
You are using an iterator that is giving you objects of type id - calling a method on those will often confuse a compiler. Are you able to cast them to a known type?
Like for(MineType* mine in mines)?
And
[tempMine setTite:[mine mineName]];
Is that a typo? My guess is you would be calling that method setTitle.
i think changing the object type of mine object in the for loop to whatever custom class you have with latitudeInitial, longitudeInitial, mineName properties/methods should solve this.