Iphone 即使不使用',您是否可以将settings.bundle中的设置设置为默认设置;t打开设置应用程序

Iphone 即使不使用',您是否可以将settings.bundle中的设置设置为默认设置;t打开设置应用程序,iphone,cocoa-touch,Iphone,Cocoa Touch,我有一个带有settings.bundle的iPhone应用程序,它可以处理我的应用程序的各种设置。我可以在root.plist文件中设置默认值(使用DefaultValue属性),但这些值仅在用户第一次打开设置应用程序时使用。有什么方法可以在应用程序安装时写出这些值?我知道我可以编写代码检查我的应用程序的首次启动,然后将它们写出来,但它们位于两个不同的位置 以下是my root.plist中的一个条目作为示例: <dict> <key>Type</key&

我有一个带有settings.bundle的iPhone应用程序,它可以处理我的应用程序的各种设置。我可以在root.plist文件中设置默认值(使用DefaultValue属性),但这些值仅在用户第一次打开设置应用程序时使用。有什么方法可以在应用程序安装时写出这些值?我知道我可以编写代码检查我的应用程序的首次启动,然后将它们写出来,但它们位于两个不同的位置

以下是my 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>

类型
psToggleSwitch说明符
标题
开放到顶部位置
钥匙
打开顶部位置
默认值
对
真值
对
虚假价值
不
最终的结果应该是,如果我要求“打开到顶部位置”,我会得到一个“是”,而不是直到用户第一次打开设置应用程序时才出现


有什么想法吗?

如果我理解正确,您希望避免将默认值指定两次(一次在Settings.bundle/Root.plist文件中作为“DefaultValue”键,一次在应用程序初始化代码中),这样您就不必保持同步

由于Settings.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];
}

这是我基于@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;
}

同一主题的多个版本。我保留了Lawrence Johnston对儿童窗格的支持,并添加了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)应用程序:(UIApplication*)应用程序使用选项完成启动:(NSDictionary*)启动选项


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

另一种方法:代码生成

下面生成一个Objective-C文件,其中包含一个注册Root.plist默认值的函数

xsltproc settings.xslt Settings.bundle/Root.plist > registerDefaults.m
可以使用XCode中的“运行脚本”构建阶段自动运行。该阶段应放在“编译源代码”之前。(xsltproc随OS X提供。)

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

settings.xslt

@“是的”,
@“不”,
@"",
@"",
无效注册表默认值(){
NSDictionary*默认值=
[NSDictionary Dictionary WithObjects和Keys:
零];
[[NSUserDefaults standardUserDefaults]注册表默认值:默认值];
}
这是基于的工作。

以下是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 2.2版本需要对字符串进行快速扩展,以通过追加PathComponent恢复
Strings:

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

我希望有一些我没有的神奇捆绑设置,比如“WriteDefaultsOnInstall”,但如果没有,这就可以了,谢谢:)这个解决方案需要注意一点——如果默认值是布尔值,它就不起作用,因为如果默认值设置为否或不存在,“if(!name)”将返回否。为了解决这个问题,我改用objectForKey,因为它将返回0、1或null。值得注意的是,
registerDefaultsFromSettingsBundle
不需要限制为条件执行:它可以安全地一直运行,因为
registerDefaults
从不覆盖现有设置;它只为没有值的设置提供默认值。但是,避免冗余执行可以节省(很少)一点时间。请注意,使用
nil
键在DefaultStoreRegister上调用
-setObject:
,将引发异常。如果
Key
存在,但
DefaultValue
不适用于给定的首选项,我相信使用上述代码会发生这种情况。例如,对于文本字段首选项(),这是有效的。我必须注意,这些东西不适用于本地化应用程序:它打开
[settingsBundle stringByAppendingPathComponent:@“Root.plist”]
,而对于本地化应用程序,您需要
localeid.lproj/Root.plist
。第二步,
[DefaultStoreRegister setObject:…]
引发一个零值异常。“prefSpecification”可能没有键“DefaultValue”,因此objectToSet将为NULL。解决方案:-如果让objectToSet:AnyObject=prefSpecification.objectForKey(“DefaultValue”)作为AnyObject?---此代码在Swift 2中不起作用<代码>无法将类型“NSMutableDictionary”的值转换为预期的参数类型“[String:AnyObject]”
如何修复?现在执行此操作…:func registerDefaultsFromSettingsBundle(){//NSLog(“从Settings.bundle注册默认值”);let defs:NSUserDefaults=NSUserDefaults.standardUserDefaults()defs.synchronize()let settingsBundle:NSString=NSBundle.mainBundle().pathForResource(“设置”,类型:“bundle”)!if(settingsBundle.containssString(“”){NSLog(“找不到设置.bundle”);return;}let Settings:NSDictionary=NSDictionary(contentsOfFile:settingsBundle.stringByAppendingPathComponent(“Root.plist”)!让首选项:NSArray=settings.objectForKey(“首选项说明符”)作为首选项{if(prefS)中prefSpecification的!NSArray让DefaultStoreRegister:NSMutableDictionary=NSMutableDictionary(容量:preferences.count)
    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)
}