【问题标题】:Having problems reading settings while using InAppSettingsKit使用 InAppSettingsKit 时出现读取设置问题
【发布时间】:2025-12-28 10:00:15
【问题描述】:

我正在使用InAppSettingsKit 模块来管理我的设置。

我可以将它包含在我的项目中,以便我可以构建一个与我的项目正确交互的设置页面。我有一个可以更改的 root.plist,当我第一次在模拟器上运行我的应用程序时,我可以看到反映的更改。

但是,在我的一生中,我无法通过我的代码访问NSUserDefaults 中的设置。当我运行下面的代码时,我可以遍历[NSUserDefaults standardUserDefaults] 的内容。但是,我只看到一堆与我的 plist 文件中的内容无关的通用设置

NSUserDefaults* d = [NSUserDefaults standardUserDefaults];

NSDictionary* dict = [d dictionaryRepresentation];

NSString* descKey;
NSString* descObject;
NSString* classTypeForKey;
NSString* classTypeForObject;
id myObject;

for (id key in dict) {
    myObject = [dict objectForKey:key];
    classTypeForObject = [[myObject class] description];
    classTypeForKey = [[key class] description];
    descKey = [key description];
    descObject = [myObject description];
}

那么 InAppSettingsKit 将设置放在哪里?医生在[NSUserDefaults standardUserDefaults] 中说,所以我很茫然。

【问题讨论】:

    标签: ios iphone objective-c nsuserdefaults inappsettingskit


    【解决方案1】:

    我认为您的问题取决于对概念的误解。 Root.plist 不存储任何设置,而只是定义 Settings.app 和 InAppSettingsKit 如何在您的 userDefaults 上工作。

    当您的应用首次启动时,没有 userDefaults。通常,您首先通过从资源中读取静态userDefaults.plist 来设置适当的默认设置:

    // Load the default values for the user defaults
    NSString* pathToUserDefaultsValues = [[NSBundle mainBundle]
                                          pathForResource:@"userDefaults" 
                                          ofType:@"plist"];
    NSDictionary* userDefaultsValues = [NSDictionary dictionaryWithContentsOfFile:pathToUserDefaultsValues];
    
    // Set them in the standard user defaults
    [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValues];
    

    【讨论】:

    • +1。我打算发布类似的东西,但这已经起来了!你应该接受这个:)
    【解决方案2】:

    这是 Ojas 的代码进一步修改以考虑 ARC。

    您可能会从 AppDelegate didFinishLaunchingWithOptions 调用 initializeSettings。

    #import "IASKSettingsReader.h"
    ...
    
    - (void)initializeSettings
    {
        // standard stored preference values
        NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    
        // settings files to process
        NSMutableArray *preferenceFiles = [[NSMutableArray alloc] init];
    
        // begin with Root file
        [preferenceFiles addObject:@"Root"];
    
        // as other settings files are discovered will be added to preferencesFiles
        while ([preferenceFiles count] > 0) {
    
            // init IASKSettingsReader for current settings file
            NSString *file = [preferenceFiles lastObject];
            [preferenceFiles removeLastObject];
            IASKSettingsReader *settingsReader = [[IASKSettingsReader alloc]
                                                  initWithFile:file];
    
            // extract preference specifiers
            NSArray *preferenceSpecifiers = [[settingsReader settingsBundle]
                                             objectForKey:kIASKPreferenceSpecifiers];
    
            // process each specifier in the current settings file
            for (NSDictionary *specifier in preferenceSpecifiers) {
    
                // get type of current specifier
                NSString *type = [specifier objectForKey:kIASKType];
    
                // need to check child pane specifier for additional file
                if ([type isEqualToString:kIASKPSChildPaneSpecifier]) {
                    [preferenceFiles addObject:[specifier objectForKey:kIASKFile]];
                }
                else {
                    // check if current specifier has a default value
                    id defaultValue = [specifier objectForKey:kIASKDefaultValue];
    
                    if (defaultValue) {
                        // get key from specifier and current stored preference value
                        NSString *key = [specifier objectForKey:kIASKKey];
                        id value = [defaults objectForKey:key];
    
                        // update preference value with default value if necessary
                        if (key && value == nil) {
                            [defaults setObject:defaultValue forKey:key];
                        }
                    }
                }
    
            }
    
        }
    
        // synchronize stored preference values
        [defaults synchronize];
    }
    

    【讨论】:

    【解决方案3】:

    现在一切都说得通了。在之前的尝试中,我曾尝试注册默认值以使事情正常进行,但我认为我可以使用 Root.plist 文件来做到这一点。但当然 registerDefaults 方法只看到 StringsTable 和 PreferenceSpecifiers 的高级条目。感谢您指出 Ortwin。

    因此,我没有构建单独的 userDefault.plist 文件,而是找到了以下代码来解析 root.plist 文件以手动将默认值添加到 NSUserDefaults。我不记得我在哪里得到它,但它似乎运作良好。如果有我不应该这样做的原因,请随时详细说明:

        - (void)setDefaults {
    
            //get the plist location from the settings bundle
            NSString *settingsPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Settings.bundle"];
            NSString *plistPath = [settingsPath stringByAppendingPathComponent:@"Root.plist"];
    
            //get the preference specifiers array which contains the settings
            NSDictionary *settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
            NSArray *preferencesArray = [settingsDictionary objectForKey:@"PreferenceSpecifiers"];
    
            //use the shared defaults object
            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
            //for each preference item, set its default if there is no value set
            for(NSDictionary *item in preferencesArray) {
    
                //get the item key, if there is no key then we can skip it
                NSString *key = [item objectForKey:@"Key"];
                if (key) {
    
                    //check to see if the value and default value are set
                    //if a default value exists and the value is not set, use the default
                    id value = [defaults objectForKey:key];
                    id defaultValue = [item objectForKey:@"DefaultValue"];
                    if(defaultValue && !value) {
                        [defaults setObject:defaultValue forKey:key];
                    }
                }
            }
    
            //write the changes to disk
            [defaults synchronize];
            }
    }
    

    【讨论】:

    • 乍一看,代码看起来没问题。但是,如果您有在单独文件中指定的子窗格,而不是Root.plist,则它将不起作用。
    • 注意你可以使用[[iaskSettingsViewController settingsReader] settingsBundle]在上面的代码中获取相应的preferencesArray
    【解决方案4】:

    我修改了 Sam 的代码以处理在单独文件中指定的子窗格:

    #import "IASKSettingsReader.h"
    
    - (void)initializeSettings
    {
        // standard stored preference values
        NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    
        // settings files to process
        NSMutableArray *preferenceFiles = [[NSMutableArray alloc] init];
    
        // begin with Root file
        [preferenceFiles addObject:@"Root"];
    
        // as other settings files are discovered will be added to preferencesFiles
        while (![preferenceFiles isEmpty]) {
    
            // init IASKSettingsReader for current settings file
            NSString *file = [[preferenceFiles lastObject] retain];
            [preferenceFiles removeLastObject];
            IASKSettingsReader *settingsReader = [[IASKSettingsReader alloc]
                                                  initWithFile:file];
            [file release];
    
            // extract preference specifiers
            NSArray *preferenceSpecifiers = [[settingsReader settingsBundle] 
                                             objectForKey:kIASKPreferenceSpecifiers]; 
    
            // process each specifier in the current settings file
            for (NSDictionary *specifier in preferenceSpecifiers) {
    
                // get type of current specifier
                NSString *type = [specifier objectForKey:kIASKType];
    
                // need to check child pane specifier for additional file
                if ([type isEqualToString:kIASKPSChildPaneSpecifier]) {
                    [preferenceFiles addObject:[specifier objectForKey:kIASKFile]];
                }
                else {
                    // check if current specifier has a default value
                    id defaultValue = [specifier objectForKey:kIASKDefaultValue];
    
                    if (defaultValue) {
                        // get key from specifier and current stored preference value
                        NSString *key = [specifier objectForKey:kIASKKey];
                        id value = [defaults objectForKey:key];
    
                        // update preference value with default value if necessary
                        if (key && value == nil) {
                            [defaults setObject:defaultValue forKey:key];                        
                        }
                    }
                }
    
            }
    
            [settingsReader release];
        }
    
        [preferenceFiles release];
    
        // synchornize stored preference values
        [defaults synchronize];
    }
    

    【讨论】:

    • +1。很好!我发布了对 ARC 代码的轻微修改。
    【解决方案5】:

    我的答案基于@Ojas 给出的答案。但我将其转换为 Swift。我希望这对使用 Swift 的人有用

    func processDefaultSettings() {
    
        let defaults = IASKSettingsStoreUserDefaults().defaults
    
        // settings files to process
        var preferenceFiles = [String]()
    
        // begin with Root file
        preferenceFiles.append("Root")
    
        // as other settings files are discovered will be added to preferencesFiles
        while !preferenceFiles.isEmpty {
    
            // init IASKSettingsReader for current settings file
            let file = preferenceFiles.last
            let settingsReader = IASKSettingsReader(file: file)
            preferenceFiles.removeLast()
    
            // extract preference specifiers
            if let preferenceSpecfiers = settingsReader.settingsDictionary[kIASKPreferenceSpecifiers] as? NSArray {
    
                // process each specifier in the current settings file
                for specifier in preferenceSpecfiers {
    
                    // get type of current specifier
                    let type = specifier[kIASKType] as! String
    
                    // need to check child pane specifier for additional file
                    if type == kIASKPSChildPaneSpecifier {
                        preferenceFiles.append(specifier[kIASKFile] as! String)
                    }
                    else {
                        // check if current specifier has a default value
                        if let defaultValue = specifier[kIASKDefaultValue] as AnyObject? {
    
                            // get key from specifier and current stored preference value
                            if let key = specifier[kIASKKey] as? String {
    
                                if let value = defaults.objectForKey(key) {
                                    // Nothing to do - already set
                                }
                                else {
                                    let appDefaults = [key: defaultValue]
                                    defaults.registerDefaults(appDefaults)
                                }
                            }
                        }
                    }
                }
            }
            // synchornize stored preference values
            defaults.synchronize()
        }
    }
    

    【讨论】:

      【解决方案6】:

      我在上面修改了史蒂夫对他自己问题的回答,以下似乎对我有用:

      在您的 IASKAppSettingsViewController 子类中-

      - (void)viewDidLoad
      {
          [super viewDidLoad];
      
      
          NSDictionary* settingsBundle = [self.settingsReader settingsBundle];
          NSArray *preferencesArray = [settingsBundle objectForKey:@"PreferenceSpecifiers"];
          NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
      
          //for each preference item, set its default if there is no value set
          for(NSDictionary *item in preferencesArray) {
      
              //get the item key, if there is no key then we can skip it
              NSString *key = [item objectForKey:@"Key"];
              if (key) {
      
                  //check to see if the value and default value are set
                  //if a default value exists and the value is not set, use the default
                  id value = [defaults objectForKey:key];
                  id defaultValue = [item objectForKey:@"DefaultValue"];
                  if(defaultValue && !value) {
                      [defaults setObject:defaultValue forKey:key];
                  }
              }
          }
      
          //write the changes to disk
          [defaults synchronize];
      

      }

      【讨论】: