无法在视图之间读取NSUserDefaults数据

时间:2011-02-25 19:26:40

标签: iphone objective-c cocoa-touch

免责声明:主要菜鸟

我正在编写一个算术闪存卡应用程序作为学习项目。我有一个UITabViewController,底部的标签栏可以在几个不同的视图之间切换。一切正常,直到我尝试在Settings视图控制器中设置NSUserDefault布尔值并尝试在Flashcards视图控制器中读取这些值。

设置视图有一个开关,用于启用/禁用每个操作符(加法,减法等),如果启用了此类操作,闪卡视图应随机显示闪存卡。

我认为我的错误在于我不理解数据封装,对象等的关键概念。我很感激任何帮助。

这是“设置”视图控制器。我甚至不确定如何将代码放入此论坛,所以这可能是一个可笑的时刻......这里是:

//  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

和...

/  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"];    
}
}

这是抽认卡视图......

//  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

和...

//  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];
}
}

3 个答案:

答案 0 :(得分:5)

这里有几件事要做。首先,您需要一种更好的方式来说明在用户做出任何明确决定之前的默认状态。接下来,您想知道何时应该根据用户首选项刷新应用内状态。

默认初始化

第一项解决方案是将默认值放入注册域中以供您的用户首选项使用。这是您在应用程序初始化期间要做的事情,而不是让您的视图单独检查首选项并在初始化时更新它们。首选项系统在很多地方查找数据,它看起来的第一个位置是仅在内存中的注册域。这是您为每个用户首​​选项设置默认值(即没有指定值表示'x')的值。

您将使用的-[NSUserDefaults registerDefaults:] API,其值为NSDictionary。要设置默认值YES(在Objective-C中,BOOL类型使用YESNO而不是TRUEFALSE),您将使用某些内容像这样,通常在应用程序主类的+initialize方法中执行:

+ (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];
}

通常你会把所有放到这样的一个方法中,所以如果你的应用程序的其他部分期望任何偏好的默认非零值,你应该将它们添加到这个组中并将其放入应用程序委托的+initialize方法。

现在,当您的其他班级使用[[NSUserDefault standardUserDefaults] boolForKey: @"additionKey"]并且没有任何内容保存到该键的首选项文件时,他们将获得上面提供的值。

刷新缓存值

因此,您可以看到用户可以更改这些首选项的视图。您的下一个工作是使上面的视图使用新的首选项更新其成员变量。为此,我们可以使用委托或通知。在这种情况下,我会通知原因:

  1. 您正在使用NSUserDefaults,理论上可以在许多不同的地方进行更改。委派只会告知您一个对象所做的更改。
  2. NSUserDefaults已经实现了一个方便的通知,您可以观看。
  3. 因此,在你的flashcardsViewController中,你将拥有类似这几种方法的东西:

    - (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];
    }
    

    <强>摘要

    总而言之,这两者应该为您提供完成这项工作所需的一切。

答案 1 :(得分:4)

Chiefly Izzy指出的“始终是”问题外,您的-buttonClicked:方法无法从NSUserDefaults中读取新值。这些值在[flashcardsViewController viewDidLoad]中读取(一次)。如果在设置中更改了它们,则在下次加载时({可能是下次启动应用程序时),将不会在flashCardsViewController中检测到更改。

最简单的方法是将您的代码移动到IsEnabled BOOL值中,并将其移动到-buttonClicked方法中,如下所示:

-(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 ...

答案 2 :(得分:3)

此代码......

//the following should assign the keys if they don't exist  
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"additionKey"]){
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"additionKey"];
}

...只需将additionKey设置为TRUE即可。如果您想检查是否设置了additionKey,请执行此操作...

//the following should assign the keys if they don't exist  
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"additionKey"]){
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"additionKey"];
}

... boolForKey:文档:如果布尔值与用户默认值中的defaultName相关联,则返回该值。否则,返回NO。

翻译为人类语言 - 如果存在与additionKey关联的值,则返回此值。如果没有关联值,则返回NO / FALSE。

因此,您的代码执行此操作 - 如果值与additionKey无关或者如果设置为NO,则将其设置为YES。这导致了这一点 - additionKey始终设置为YES / TRUE。