即使您没有打开设置应用程序,您是否可以在Settings.bundle默认设置中进行设置

我有一个iPhone应用程序与settings.bundle处理我的应用程序的各种设置。 我可以在我的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> 

最终结果应该是,如果我要求“open_to_top_location”,我会得到一个YES,而不是根本没有,直到第一次用户打开设置应用程序。

有任何想法吗?

如果我正确地理解了你,你想要避免有两次指定的默认值(一次在你的Settings.bundle / Root.plist文件中的“DefaultValue”键,一次在你的应用程序初始化代码中),所以你不必保留它们同步。

由于Settings.bundle存储在应用程序包本身中,所以您可以只读取那里给出的默认值。 我将一些示例代码放在一起,查看Settings包并读取每个键的默认值。 请注意,这不会写出默认的键; 如果它们不存在,则需要在每次启动时阅读并注册它们(请随时更改)。 我只做了一些粗略的testing,所以在任何情况下都要确保它适合你。

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

这是我的代码基于@ PCheese的答案 ,它增加了对没有默认值和子窗格的键的支持。

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

这里是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() } 

一个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() } 

Swift 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) } 

同一主题的另一个版本。 我保留了劳伦斯·约翰斯顿(Lawrence Johnston)对儿童窗格的支持,并增加了国际化的支持。

 // 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]; from - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

if(x) {NSAssert(x);return nil;}看起来很愚蠢,但我懒得去做一些事情。

一种不同的方法:代码生成

下面用一个函数生成一个Objective-C文件,注册Root.plist的默认值。

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

在XCode中可以使用“运行脚本”构build阶段自动运行。 阶段应放在“编译源”之前。 (xsltproc附带OS X.)

“运行脚本”屏幕截图

这是有点基本的,不处理嵌套的文件,但也许有人有它的用处。

settings.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)的工作。

stringByAppendingPathComponent swift 2.2版本,需要对string进行快速扩展才能恢复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) }