代码之家  ›  专栏  ›  技术社区  ›  rustyshelf

即使没有打开设置应用程序,也可以在settings.bundle默认设置中进行设置吗?

  •  55
  • rustyshelf  · 技术社区  · 17 年前

    我有一个带有settings.bundle的iPhone应用程序,它可以处理我的应用程序的各种设置。我可以在root.plist文件中设置默认值(使用defaultvalue属性),但这些值只能在用户第一次打开设置应用程序时使用。在应用程序安装时,是否有任何方法可以写出这些值?我知道我可以编写代码来检查我的应用程序的第一次发布,然后写出来,但是它们在两个不同的地方。

    下面是我的root.plist中的一个条目,例如:

    <dict>
        <key>Type</key>
        <string>PSToggleSwitchSpecifier</string>
        <key>Title</key>
        <string>Open To Top Location</string>
        <key>Key</key>
        <string>open_top_location</string>
        <key>DefaultValue</key>
        <string>YES</string>
        <key>TrueValue</key>
        <string>YES</string>
        <key>FalseValue</key>
        <string>NO</string>
    </dict>
    

    最终的结果应该是,如果我要求“打开到顶部位置”,我会得到一个“是”,而不是直到用户第一次打开设置应用程序时它才出现。

    有什么想法吗?

    8 回复  |  直到 9 年前
        1
  •  93
  •   community wiki 3 revs, 2 users 97% PCheese    13 年前

    如果我理解正确,您希望避免在settings.bundle/root.plist文件中指定两次默认值(一次是“defaultvalue”键,另一次是在应用程序初始化代码中),这样就不必使它们保持同步。

    由于settings.bundle存储在app bundle本身中,因此您可以只读取此处给定的默认值。我将一些示例代码放在一起,查看设置包并读取其中每个键的默认值。请注意,这不会写出默认的密钥;如果它们不存在,您将需要在每次启动时读取并注册它们(请随意更改)。我只做了一些粗略的测试,所以请确保它在所有情况下都适用。

    - (void)applicationDidFinishLaunching:(UIApplication *)application {    
        NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:@"name"];
        NSLog(@"name before is %@", name);
    
        // Note: this will not work for boolean values as noted by bpapa below.
        // If you use booleans, you should use objectForKey above and check for null
        if(!name) {
            [self registerDefaultsFromSettingsBundle];
            name = [[NSUserDefaults standardUserDefaults] stringForKey:@"name"];
        }
        NSLog(@"name after is %@", name);
    }
    
    - (void)registerDefaultsFromSettingsBundle {
        NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
        if(!settingsBundle) {
            NSLog(@"Could not find Settings.bundle");
            return;
        }
    
        NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
        NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
    
        NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
        for(NSDictionary *prefSpecification in preferences) {
            NSString *key = [prefSpecification objectForKey:@"Key"];
            if(key && [[prefSpecification allKeys] containsObject:@"DefaultValue"]) {
                [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
            }
        }
    
        [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
        [defaultsToRegister release];
    }
    
        2
  •  12
  •   Community Mohan Dere    9 年前

    这是我基于@pcheese的代码 answer 这增加了对没有默认值和子窗格的键的支持。

    - (void)registerDefaultsFromSettingsBundle {
        [[NSUserDefaults standardUserDefaults] registerDefaults:[self defaultsFromPlistNamed:@"Root"]];
    }
    
    - (NSDictionary *)defaultsFromPlistNamed:(NSString *)plistName {
        NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
        NSAssert(settingsBundle, @"Could not find Settings.bundle while loading defaults.");
    
        NSString *plistFullName = [NSString stringWithFormat:@"%@.plist", plistName];
    
        NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:plistFullName]];
        NSAssert1(settings, @"Could not load plist '%@' while loading defaults.", plistFullName);
    
        NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
        NSAssert1(preferences, @"Could not find preferences entry in plist '%@' while loading defaults.", plistFullName);
    
        NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
        for(NSDictionary *prefSpecification in preferences) {
            NSString *key = [prefSpecification objectForKey:@"Key"];
            id value = [prefSpecification objectForKey:@"DefaultValue"];
            if(key && value) {
                [defaults setObject:value forKey:key];
            } 
    
            NSString *type = [prefSpecification objectForKey:@"Type"];
            if ([type isEqualToString:@"PSChildPaneSpecifier"]) {
                NSString *file = [prefSpecification objectForKey:@"File"];
                NSAssert1(file, @"Unable to get child plist name from plist '%@'", plistFullName);
                [defaults addEntriesFromDictionary:[self defaultsFromPlistNamed:file]];
            }        
        }
    
        return defaults;
    }
    
        3
  •  9
  •   yanko    11 年前

    以下是Swift版本: 叫它:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        self.registerDefaultsFromSettingsBundle()
    
        return true
    }
    

    转换函数:

    func registerDefaultsFromSettingsBundle(){
        //NSLog("Registering default values from Settings.bundle");
        let defs: NSUserDefaults = NSUserDefaults.standardUserDefaults()
        defs.synchronize()
    
        var settingsBundle: NSString = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")!    
        if(settingsBundle.containsString("")){
            NSLog("Could not find Settings.bundle");
            return;
        }
        var settings: NSDictionary = NSDictionary(contentsOfFile: settingsBundle.stringByAppendingPathComponent("Root.plist"))!
        var preferences: NSArray = settings.objectForKey("PreferenceSpecifiers") as NSArray
        var defaultsToRegister: NSMutableDictionary = NSMutableDictionary(capacity: preferences.count)
    
        for prefSpecification in preferences {
            if (prefSpecification.objectForKey("Key") != nil) {
                let key: NSString = prefSpecification.objectForKey("Key")! as NSString
                if !key.containsString("") {
                    let currentObject: AnyObject? = defs.objectForKey(key)
                    if currentObject == nil {
                        // not readable: set value from Settings.bundle
                        let objectToSet: AnyObject? = prefSpecification.objectForKey("DefaultValue")
                        defaultsToRegister.setObject(objectToSet!, forKey: key)
                        NSLog("Setting object \(objectToSet) for key \(key)")
                    }else{
                        //already readable: don't touch
                        //NSLog("Key \(key) is readable (value: \(currentObject)), nothing written to defaults.");
                    }
                }
            }
        }
        defs.registerDefaults(defaultsToRegister)
        defs.synchronize()
    }
    
        4
  •  4
  •   Anders Cedronius    9 年前

    斯威夫特3版

        func registerDefaultsFromSettingsBundle(){
        guard let settingsBundle = Bundle.main.path(forResource: "Settings", ofType: "bundle") else {
            print("Could not locate Settings.bundle")
            return
        }
    
        guard let settings = NSDictionary(contentsOfFile: settingsBundle+"/Root.plist") else {
            print("Could not read Root.plist")
            return
        }
    
        let preferences = settings["PreferenceSpecifiers"] as! NSArray
        var defaultsToRegister = [String: AnyObject]()
        for prefSpecification in preferences {
            if let post = prefSpecification as? [String: AnyObject] {
                guard let key = post["Key"] as? String,
                    let defaultValue = post["DefaultValue"] else {
                        continue
                }
                defaultsToRegister[key] = defaultValue
            }
        }
        UserDefaults.standard.register(defaults: defaultsToRegister)
    }
    
        5
  •  3
  •   JTango18    10 年前

    Swift 2兼容版本

    func registerDefaultsFromSettingsBundle(){
    
        let defaults = NSUserDefaults.standardUserDefaults()
        defaults.synchronize()
    
        let settingsBundle: NSString = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")!
        if(settingsBundle.containsString("")){
            NSLog("Could not find Settings.bundle");
            return;
        }
        let settings = NSDictionary(contentsOfFile: settingsBundle.stringByAppendingPathComponent("Root.plist"))!
        let preferences = settings.objectForKey("PreferenceSpecifiers") as! NSArray;
        var defaultsToRegister = [String: AnyObject](minimumCapacity: preferences.count);
    
        for prefSpecification in preferences {
            if (prefSpecification.objectForKey("Key") != nil) {
                let key = prefSpecification.objectForKey("Key")! as! String
                if !key.containsString("") {
                    let currentObject = defaults.objectForKey(key)
                    if currentObject == nil {
                        // not readable: set value from Settings.bundle
                        let objectToSet = prefSpecification.objectForKey("DefaultValue")
                        defaultsToRegister[key] = objectToSet!
                        NSLog("Setting object \(objectToSet) for key \(key)")
                    }
                }
            }
        }
        defaults.registerDefaults(defaultsToRegister)
        defaults.synchronize()
    }
    
        6
  •  0
  •   18446744073709551615    14 年前

    同一主题的另一个版本。我保留了劳伦斯约翰斯顿对儿童窗格的支持,并添加了i18n/l10n支持。

    // This code is folklore, first created by an unknown person and copied, pasted
    // and published by many different programmers, each (hopefully) of whom added
    // some improvemrnts. (c) the People of the Earth
    - (NSDictionary *)defaultsFromPlistNamed:(NSString *)plistName {
        NSString *settingsBundlePath = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
        if (!settingsBundlePath) {
            NSAssert(settingsBundlePath, @"Could not find Settings.bundle while loading defaults.");
            return nil;
        }
    
        NSBundle *settingsBundle = [NSBundle bundleWithPath:settingsBundlePath];
        if (!settingsBundlePath) {
            NSAssert(settingsBundle, @"Could not load Settings.bundle while loading defaults.");
            return nil;
        }
    
        NSString *plistFullName = [settingsBundle pathForResource:plistName ofType:@"plist"];
        if (!plistName) {
            NSAssert1(settings, @"Could not find plist '%@' while loading defaults.", plistFullName);
            return nil;
        }
    
        NSDictionary *settings_dic = [NSDictionary dictionaryWithContentsOfFile:plistFullName];
        if (!settings_dic) {
            NSAssert1(settings_dic, @"Could not load plist '%@' while loading defaults.", plistFullName);
            return nil;
        }
    
        NSArray *preferences = [settings_dic objectForKey:@"PreferenceSpecifiers"];
        NSAssert1(preferences, @"Could not find preferences entry in plist '%@' while loading defaults.", plistFullName);
    
        NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
        for(NSDictionary *prefSpecification in preferences) {
            NSString *key = [prefSpecification objectForKey:@"Key"];
            if (key) {
                id value = [prefSpecification objectForKey:@"DefaultValue"];
                if(value) {
                    [defaults setObject:value forKey:key];
                    NSLog(@"setting %@ = %@",key,value);
                } 
            }
    
            NSString *type = [prefSpecification objectForKey:@"Type"];
            if ([type isEqualToString:@"PSChildPaneSpecifier"]) {
                NSString *file = [prefSpecification objectForKey:@"File"];
                NSAssert1(file, @"Unable to get child plist name from plist '%@'", plistFullName);
                if (file) {
                    [defaults addEntriesFromDictionary:[self defaultsFromPlistNamed:file]];
                }
            }        
        }
    
        return defaults;
    }
    
    - (void)registerDefaultsFromSettingsBundle {
        [[NSUserDefaults standardUserDefaults] registerDefaults:[self defaultsFromPlistNamed:@"Root"]];
    }
    

    呼叫 [self registerDefaultsFromSettingsBundle]; 从 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    if(x) {NSAssert(x);return nil;} 看起来很蠢,但我觉得懒得做点什么。

        7
  •  0
  •   nschum Solin    14 年前

    另一种方法:代码生成

    下面生成一个带有一个函数的objective-c文件,该函数为root.plist注册默认值。

    xsltproc settings.xslt Settings.bundle/Root.plist > registerDefaults.m
    

    可以使用xcode中的“运行脚本”构建阶段自动运行。这个阶段应该放在“编译源”之前。(xsltproc附带OS X。)

    "Run Script" screenshot

    这有点基础,不处理嵌套文件,但可能有人使用它。

    设置XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes" indent="no" />
    
      <xsl:template match="dict">
        <xsl:choose>
          <xsl:when test="key[.='DefaultValue']/following-sibling::*[position()=1 and self::true]">
            @"YES",
          </xsl:when>
          <xsl:when test="key[.='DefaultValue']/following-sibling::*[position()=1 and self::false]">
            @"NO",
          </xsl:when>
          <xsl:otherwise>
            @"<xsl:value-of select="key[.='DefaultValue']/following-sibling::*[1]"/>",
          </xsl:otherwise>
        </xsl:choose>
        @"<xsl:value-of select="key[.='Key']/following-sibling::*[1]"/>",
      </xsl:template>
    
      <xsl:template match="/">
        void registerDefaults() {
    
        NSDictionary *defaults =
        [NSDictionary dictionaryWithObjectsAndKeys:
        <xsl:apply-templates select="descendant::key[.='DefaultValue']/.."/>
        nil];
    
        [[NSUserDefaults standardUserDefaults] registerDefaults: defaults];
        }
      </xsl:template>
    
    </xsl:stylesheet>
    

    基于的工作 Benjamin Ragheb .

        8
  •  0
  •   JuJoDi    10 年前

    更干净的swift 2.2版本需要对字符串进行快速扩展才能恢复 stringByAppendingPathComponent :

    extension String {
        func stringByAppendingPathComponent(path: String) -> String {
            let nsSt = self as NSString
            return nsSt.stringByAppendingPathComponent(path)
        }
    }
    
    func registerDefaultsFromSettingsBundle() {
        guard let settingsBundle = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle") else {
            log.debug("Could not find Settings.bundle")
            return
        }
    
        let settings = NSDictionary(contentsOfFile: settingsBundle.stringByAppendingPathComponent("Root.plist"))!
    
        let preferences = settings["PreferenceSpecifiers"] as! NSArray
    
        var defaultsToRegister = [String: AnyObject]()
    
        for prefSpecification in preferences {
            guard let key = prefSpecification["Key"] as? String,
            let defaultValue = prefSpecification["DefaultValue"] else {
                continue
            }
    
            defaultsToRegister[key] = defaultValue
        }
        NSUserDefaults.standardUserDefaults().registerDefaults(defaultsToRegister)
    }