Preferences and Settings Programming Guide

关于首选项和设置

首选项是您持久存储的信息,并用于配置您的应用程序。应用程序通常会向用户公开偏好设置,以便他们自定义应用程序的外观和行为。大多数首选项都使用Cocoa首选项系统(称为用户默认系统)本地存储。应用程序还可以使用键值存储在用户的iCloud帐户中存储首选项。

用户默认系统和键值存储都设计用于在属性列表中存储简单数据类型 - 字符串,数字,日期,布尔值,URL,数据对象等。使用属性列表也意味着您可以使用数组和字典类型来组织偏好数据。首先将其编码到NSData对象中,也可以将其他对象存储在属性列表中。

概述

应用程序可以通过多种方式集成首选项,包括以编程方式覆盖整个代码中的各个方面,并作为用户界面的一部分。 iOS和Mac应用都支持偏好设置。

你决定你想要设置什么偏好

每个应用的偏好设置都不同,您可以决定要配置的应用程序的哪些部分。配置包括从代码中检查存储的首选项的值,并根据该值执行操作。因此,偏好值本身应该始终是简单的,并具有特定的意义,然后由您的应用程序实现。

相关部分:什么是好的偏好?

应用提供自己的首选项界面

因为每个应用程序的偏好都不同,所以应用程序本身负责决定如何最好地向用户呈现这些偏好设置(如果有的话)。 iOS和OS X都提供了一些标准的地方,您可以使用首选项界面,但您仍然负责设计该界面并在适当的时间显示该界面。

相关部分:提供首选界面

应用程序访问首选项使用用户默认对象

应用程序使用用户默认对象(即NSUserDefaults对象(iOS和OS X))或NSUserDefaultsController对象(仅限OS X)访问本地存储的首选项。除了检索偏好值之外,应用程序还可以使用此对象注册首选项的默认值,并管理首选项系统的其他方面。

相关章节:访问偏好值

关于用户默认系统

用户默认系统管理每个用户的首选项的存储。大多数偏好设置都会持续存储,因此在应用程序的后续启动周期内不会更改。应用程序使用首选项来跟踪用户启动的和程序启动的配置更改。

什么是好的偏好?

在定义应用程序的首选项时,最好尽可能使用简单的值和数据类型。首选项系统围绕属性列表数据类型构建,如字符串,数字和日期。虽然您可以使用NSData对象在首选项中存储任意对象,但在大多数情况下不建议执行此操作。

永久存储对象意味着您的应用程序必须在某个时间对该对象进行解码。在首选项的情况下,存储的对象意味着每当您访问该偏好时对该对象进行解码。这也意味着您的应用程序的较新版本必须确保能够使用较早版本的应用程序对创建和写入磁盘的对象进行解码,这可能会容易出错。

更好的方法是存储简单的字符串和值,并使用它们来创建应用程序需要的对象。存储简单的值意味着您的应用程序可以随时访问该值。从发布到发布的唯一的事情就是解释简单的值和应用程序创建的对象。

提供首选接口

对于面向用户的首选项,表1-1列出了向用户显示这些首选项的选项。从这个表中可以看出,大多数选项都涉及创建用于管理和呈现首选项的自定义用户界面。如果您正在创建iOS应用程序,则可以使用“设置”软件包来显示首选项,但是您应该仅在用户不经常更改的设置时执行此操作。

表1-1为用户显示首选项的选项
偏爱
iOS版
OS X
经常更改的首选项
自定义UI
自定义UI
不经常更改的首选项
设置包
自定义UI
注意:可能会频繁更改的首选项的示例包括游戏的音量级别或控制选项。邮件应用程序中的电子邮件地址和服务器设置可能不频繁更改的首选项的示例。对于iOS应用程序,您最终可以决定是否适用于从“设置”应用程序或应用程序内部显示偏好设置。
应用程序菜单中的“首选项”菜单项可以访问Mac应用程序中的首选项。使用Xcode模板创建的Cocoa应用程序会自动为您提供一个菜单项。当用户选择此菜单项时,您有责任提供适当的用户界面。您可以通过在应用程序委托中定义一个操作方法来提供该用户界面,该方法显示自定义首选项窗口,并将该操作方法连接到Interface Builder中的菜单项。

没有标准的方式可以在iOS应用程式内显示自订偏好设定。您可以通过多种方式集成首选项,包括在标签栏界面中使用单独的选项卡,或者使用应用程序屏幕之一中的自定义按钮。通常应使用不同的视图控制器来呈现首选项,以便当该视图控制器被用户忽略时可以记录首选项的改变。

偏好组织

首选项分为域,每个域具有名称和特定用途。例如,有一个应用程序特定首选项的域,另一个适用于所有应用程序的系统范围的首选项。所有的偏好设置都是基于每个用户存储和访问的。不支持在用户之间共享首选项。

每个偏好有三个组成部分:

存储它的域
它的名称(指定为NSString对象)
它的值可以是任何属性列表对象(NSData,NSString,NSNumber,NSDate,NSArray或NSDictionary)
首选项的生命周期取决于您存储的域。某些域通过将首选项写入用户的默认数据库来持久存储首选项。这种喜好从一个应用程序启动到下一个应用程序仍然存在。其他域以更易变的方式存储偏好,仅在相应的用户默认对象的生命周期内保留偏好值。

搜索给定首选项的值通过NSUserDefaults对象的搜索列表中的域进行。只搜索搜索列表中的域,并按照表1-2所示的顺序进行搜索,从NSArgumentDomain域开始。当找到具有指定名称的首选项时,搜索结束。如果多个域包含相同的首选项,则该值取自离搜索列表开头最近的域。

表1-2域的搜索顺序


NSArgumentDomain
挥发物
应用程序(由应用程序的标识符识别)
一贯
NSGlobalDomain
一贯
语言(由语言名称识别)
挥发物
NSRegistrationDomain
挥发物
参数域
参数域包括从命令行参数设置的值(如果您从命令行启动应用程序),并由NSArgumentDomain常量标识。从命令行设置的值将被系统自动放入此域。要向此域添加值,请在命令行中指定首选项名称(在连字符前面),并使用相应的值进行跟踪。例如,以下命令启动Xcode并将其IndexOnOpen首选项的值设置为NO:

localhost> Xcode.app/Contents/MacOS/Xcode-IndexOnOpen NO
从命令行设置的首选项临时覆盖存储在用户的默认数据库中的已建立的值。在上述示例中,将IndexOnOpen首选项设置为NO会阻止Xcode自动对项目建立索引,即使在用户默认数据库中将首选项设置为YES。

应用程序域

应用程序域包含存储在当前用户的用户默认数据库中的特定于应用程序的首选项。当您使用共享的NSUserDefaults对象(或OS X中的NSUserDefaultsController对象)来编写首选项时,这些首选项将自动放置在此域中。

由于此域名是应用程序特定的,因此域的内容与您的应用程序的包标识符相关。该域的内容存储在由系统管理的文件中。目前,该文件位于$ HOME / Library / Preferences /目录中,其中$ HOME是应用程序的主目录或用户的主目录(取决于平台以及您的应用程序是否在沙箱中)。用户默认数据库文件的名称为<ApplicationBundleIdentifer> .plist,其中<ApplicationBundleIdentifer>是您的应用程序的包标识符。您不应该直接修改此文件,但可以在调试期间检查它,以确保您的应用程序正在写入偏好值。

全球域名

全局域包含适用于所有应用程序的首选项,并由NSGlobalDomain常量标识。系统框架通常使用此域来存储系统范围的值,应用程序不应使用该域来存储特定于应用程序的值。如果要在全局域中更改首选项的值,请使用新值将相同的首选项写入应用程序域。

系统框架如何使用此域的示例:

NSRuleView类的实例将用户的首选测量单位存储在AppleMeasurementUnits键中。使用此存储位置会导致所有应用程序中的标尺视图使用相同的单位。
系统使用AppleLanguages键将用户的首选语言存储为字符串数组。例如,用户可以将英语指定为首选语言,其次是西班牙语,法语,德语,意大利语和瑞典语。
语言域
对于AppleLanguages首选项中的每种语言,系统会在名称基于语言名称的域中记录特定于语言的首选项值。每个特定于语言的域包含相应区域设置的首选项。 Foundation框架中的许多类(如NSDate,NSDateFormatter,NSTimeZone,NSString和NSScanner类)使用此语言环境信息来修改其行为。例如,当您请求NSCalendarDate对象的字符串表示时,NSCalendarDate对象使用区域设置信息来查找用户首选语言的月份和星期几的名称。

注册域

如果在其他域中未明确设置给定的首选项,则注册域定义要使用的一组默认值。在启动时,应用程序可以调用NSUserDefaults的registerDefaults:方法为重要的首选项指定一组默认值。当应用程序第一次启动时,大多数首选项都没有值,因此检索它们将产生未定义的结果。注册一组默认值可确保您的应用程序始终具有已知的一组值来操作。

只能使用registerDefaults:方法设置注册域的内容。

使用默认工具查看首选项

在OS X中,默认的命令行工具提供了一种检查用户默认数据库内容的方法。 在应用程序开发过程中,您可以使用此工具来验证应用程序正在写入磁盘的首选项。 为此,您可以使用终端应用程序中以下表单的命令:

默认值为<application-bundle-identifier>

要阅读全局域的内容,您可以使用以下命令:

默认值读取NSGlobalDomain

有关使用默认工具读取和写入首选项值的更多信息,请参阅默认值手册页。

访问偏好值

您使用NSUserDefaults类来访问您的应用程序的首选项。每个应用程序都提供了此类的单个实例,可从standardUserDefaults类方法访问。您可以使用共享用户默认对象:

在启动时指定应用程序偏好设置的任何默认值。
获取并设置存储在应用程序域中的个人偏好值。
删除首选项值。
检查volatile偏好域的内容。
使用Cocoa绑定的Mac应用程序可以使用NSUserDefaultsController对象自动设置和获取首选项。您通常将此类对象添加到用于显示面向用户的首选项的相同nib文件。将用户界面控件绑定到用户默认控制器中的项目,该控件处理在用户默认数据库中获取和设置值的过程。

首选项值必须是标准属性列表对象类型之一:NSData,NSString,NSNumber,NSDate,NSArray或NSDictionary。 NSUserDefaults类还提供内置操作,用于将NSURL对象存储为首选项值。有关属性列表及其内容的更多信息,请参阅“属性列表编程指南”。

注册您的应用程序的默认首选项

在发布时,一个应用程序应该为其期望存在和有效的任何首选项注册默认值。当您请求从未设置的首选项的值时,NSUserDefaults类的方法返回适用于数据类型的默认值。对于数字标量值,这通常意味着返回0,但对于字符串和其他对象,它意味着返回零。如果这些标准默认值不适用于您的应用程序,则可以使用registerDefaults:方法注册自己的默认值。此方法将您的自定义默认值放置在NSRegistrationDomain域中,这将导致在未明确设置首选项时返回它们。

调用registerDefaults:方法时,必须提供一个您需要注册的所有默认值的字典。清单2-1显示了一个iOS应用程序在启动周期早期注册其默认值的示例。您可以随时注册默认值,但在尝试检索任何首选项值之前,应始终注册它们。

清单2-1注册默认首选项值

   // Register the preference defaults early.
    NSDictionary *appDefaults = [NSDictionary
        dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"CacheDataAgressively"];
    [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];

   // Other initialization...
}

在为标量类型注册默认值时,使用NSNumber对象来指定数字的值。如果要注册其值为URL的首选项,请先使用NSKeyedArchiver的archivedDataWithRootObject:方法对NSData对象中的URL进行编码。虽然您可以对其他类型的对象使用类似的技术,但是当有更简单的选项可用时,您应该避免这样做。

获取和设置偏好值

您可以使用NSUserDefaults类的方法获取并设置首选项值。此类具有获取和设置类型为Boolean,integer,float和double的标量值的首选项的方法。它还具有获取和设置其值为NSData,NSDate,NSString,NSNumber,NSArray,NSDictionary和NSURL类型的对象的首选项的方法。有两种情况可以获得偏好值,您可以在其中设置它们:

获取偏好值:

当您需要使用该值配置您的应用程序的行为。
当您需要在首选项界面中显示该值时。
当用户在首选项界面中更改它们时,设置首选项值。
以下代码显示了如何在代码中获取首选项值。在此示例中,代码检索CacheDataAggressively键的值,该值是应用程序可能用于确定其缓存策略的自定义键。像这样的代码可以在任何地方使用来处理应用程序的自定义配置。如果要向用户显示此特定偏好值,则可以使用类似的代码来配置首选项界面的控件。

if ([[NSUserDefaults standardUserDefaults] boolForKey:@"CacheDataAggressively"]) {
   // Delete the backup file.
}

要以编程方式设置首选项值,可以调用NSUserDefaults的相应设置器方法。 设置对象值时,必须使用setObject:forKey:方法。 调用此方法时,必须确保对象是标准属性列表类型之一。 以下示例基于应用程序的首选项界面的状态设置一些首选项。

NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
if ([cacheAgressivelyButton state] == NSOnState) {
   // The user wants to cache files aggressively.
   [defaults setBool:YES forKey:@"CacheDataAggressively"];
   [defaults setObject:[NSDate dateWithTimeIntervalSinceNow:(3600 * 24 * 7)]
             forKey:@"CacheExpirationDate"]; // Set a 1-week expiration
} else {
    // The user wants to use lazy caching.
   [defaults setBool:NO forKey:@"CacheDataAggressively"];
   [defaults removeObjectForKey:@"CacheExpirationDate"];
}

您不必显示首选项界面来管理所有值。您的应用程序可以使用首选项来缓存有趣的信息。例如,NSWindow对象将其当前位置存储在用户默认系统中。该数据允许他们在下次用户启动应用程序时返回到相同的位置。

同步和检测偏好变化

由于NSUserDefaults类缓存值,有时需要将缓存的值与用户默认数据库的当前内容进行同步。您的应用程序并不总是修改用户默认数据库的唯一实体。在iOS中,“设置”应用可以修改具有“设置”包的应用的首选项的值。在OS X中,系统和其他应用程序可能会修改首选项值以响应用户操作。例如,如果用户更改首选语言,则系统将新值写入用户默认数据库。在OS X v10.5及更高版本中,共享的NSUserDefaults对象将以周期性间隔自动同步其缓存。但是,应用程序可以手动调用synchronized方法来强制更新缓存的值。

要检测何时发生偏好值的更改,应用程序也可以注册通知NSUserDefaultsDidChangeNotification。共享的NSUserDefaults对象将发送此通知到您的应用程序,只要它检测到位于其中一个持久性域中的首选项的更改。您可以使用此通知来响应可能影响用户界面的更改。例如,您可以使用它来检测用户首选语言的更改,并适当更新您的应用内容。

使用Cocoa绑定管理首选项

Mac应用程序可以使用Cocoa绑定从其用户界面直接设置首选项值。使用绑定修改首选项涉及将NSUserDefaultsController对象添加到适当的nib文件,并将控件的值绑定到用户默认数据库中的首选项值。当您的应用程序显示界面时,用户默认控制器会自动从用户默认数据库加载值,并使用它们设置控件的值。类似地,当用户更改控件中的值时,用户默认控制器更新用户默认数据库中的值。

有关如何使用NSUserDefaultsController类将优选值绑定到用户界面的更多信息,请参阅Cocoa Bindings编程主题中的用户默认值和绑定。

使用Core Foundation管理首选项

核心基础框架提供了自己的一组接口,用于访问存储在用户默认数据库中的首选项。NSUserDefaults类一样,您可以使用Core Foundation函数来获取和设置首选项值并同步用户默认数据库与NSUserDefaults不同,您可以使用Core Foundation函数为不同的应用程序和不同的计算机编写首选项。请注意,修改某些首选项域(不属于当前应用程序和用户的域)需要root权限(或OS X v10。 6之前的管理员权限);有关如何获得适当权限的信息,请参阅授权服务编程指南在沙箱中安装的应用程序无法对应用程序域名进行写入。

有关获取和设置首选项的核心基础功能的信息,请参阅“首选项实用程序参考”。

使用核心基础设置偏好值

首选存储为键值对,密钥必须是CFString对象,但该值可以是任何Core Foundation属性列表值(请参阅Core Foundation的“属性列表编程主题”),包括容器类型例如,您可能会使用一个名为defaultWindowWidth的键来定义应用程序创建的任何新窗口的宽度(以像素为单位)。其值很可能是CFNumber类型。您还可以决定将窗口宽度和高度合并为名为defaultWindowSize的单个首选项,并将其值设为包含两个CFNumber对象的CFArray对象。

清单2-2中的代码演示了如何为应用程序MyTextEditor创建一个简单的首选项。该示例将应用程序的默认文本颜色设置为蓝色。

清单2-2编写一个简单的默认值

CFStringRef textColorKey = CFSTR("defaultTextColor");
CFStringRef colorBLUE = CFSTR("BLUE");

// Set up the preference.
CFPreferencesSetAppValue(textColorKey, colorBLUE,
        kCFPreferencesCurrentApplication);

// Write out the preference data.
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);

请注意,CFPreferencesSetAppValue本身不足以创建新的首选项。需要调用CFPreferencesAppSynchronize来实际保存该值。如果您正在编写多个首选项,则在设置最后一个值后才能同步一次,而不是在设置每个单独的值后进行同步。例如,如果实现首选项窗格,则只有当用户按下“确定”按钮时,才能同步。在其他情况下,您可能不想在应用程序退出之前完全同步,但请注意,如果应用程序崩溃,所有未保存的首选项设置将丢失。

使用核心基础获得偏好值
找到和检索首选项值的最简单的方法是使用CFPreferencesCopyAppValue函数。此调用按顺序搜索各种偏好域,直到找到您指定的密钥。如果在不太特定的域 - 任何应用程序中设置了首选项,例如 - 如果找不到更具体的版本,则使用此调用检索值。清单2-3显示了如何检索列表2-2中保存的文本颜色偏好。

列表2-3读取简单的默认值

CFStringRef textColorKey = CFSTR("defaultTextColor");
CFStringRef textColor;

// Read the preference.
textColor = (CFStringRef)CFPreferencesCopyAppValue(textColorKey,
        kCFPreferencesCurrentApplication);
// When finished with value, you must release it
// CFRelease(textColor);

从首选项返回的所有值都是不可变的,即使您刚刚使用可变对象设置值。

在iCloud中存储首选项

应用程序可以使用iCloud键值存储在用户的其他计算机和iOS设备上与其他本身的实例共享少量数据。键值存储用于简单的数据类型,如可能用于首选项的数据类型。例如,杂志应用程序可能会存储用户正在读取的当前问题和页码,以便应用程序的其他实例可以在启动时打开到同一页面。您不应该将此商店用于大量数据或复杂数据类型。

要使用iCloud键值存储,请执行以下操作:

在Xcode中,为您的应用配置com.apple.developer.ubiquity-kvstore-identifier权限。
在您的代码中,创建共享的NSUbiquitousKeyValueStore对象并注册更改通知。
使用NSUbiquitousKeyValueStore的方法来获取和设置值。
iCloud中的键值数据仅限于简单的属性列表类型(字符串,数字,日期等)。

使用iCloud Key-Value Store的策略

键值存储不用于存储大量数据。它用于存储配置数据,首选项和少量应用程序相关数据。为了帮助您确定键值存储是否适合您的需要,请考虑以下事项:

每个应用程序在键值存储区中的总空间限制为1 MB。 (还有一个单独的每键限制为1 MB,最多允许1024个键。)因此,您不能使用键值存储来共享大量数据。
键值存储仅支持属性列表类型。属性列表类型包括简单类型,如NSNumber,NSString和NSDate对象。您还可以将原始数据块存储在NSData对象中,并使用NSArray和NSDictionary对象排列所有类型。
键值存储用于存储不经常更改的数据。如果设备上的应用程序频繁更改密钥值存储,则系统可能会延迟某些更改的同步,以便最小化到服务器的往返次数。应用程序更频繁地进行更改,更有可能后来的更改将被延迟,而不会立即显示在其他设备上。
键值存储不是替换用于保存相同数据的首选项或其他本地技术。键值存储的目的是在应用程序之间共享数据,但如果iCloud未启用或在给定设备上不可用,则仍然可能需要保留本地数据副本。
如果您使用键值存储来共享首选项,则一种方法是将实际值存储在用户默认数据库中,并使用键值存储进行同步。 (如果不想使用首选项系统,还可以将更改保存在自定义属性列表文件或其他本地存储中。)在本地更改密钥值时,将该更改写入用户默认值数据库和iCloud键值存储在同一时间。要从外部来源接收更改,请为通知NSUbiquitousKeyValueStoreDidChangeExternalNotification添加观察器,并使用您的处理程序方法来检测哪些键在外部更改,并更新用户默认数据库中的相应数据。通过这样做,您的用户默认数据库始终包含正确的配置值。 iCloud键值存储只是确保用户默认数据库具有最新更改的机制。

配置您的应用程序以使用键值存储

为了使用键值存储,必须使用com.apple.developer.ubiquity-kvstore-identifier授权显式配置应用程序。您可以使用Xcode启用此权限,并为应用程序指定其值,如“应用程序发布指南”中添加iCloud支持中所述。

当您启用键值存储时,Xcode将自动填充基于应用程序的捆绑包标识符的容器字段的默认值。对于大多数应用,默认值是您想要的。但是,如果您的应用程序与另一个应用程序共享其键值存储空间,则必须指定其他应用程序的软件包标识符。例如,如果您有一个应用程序的精简版本,您可能希望它使用与付费版本相同的键值存储。

启用权限是使用共享的NSUbiquitousKeyValueStore对象所必需的。只要配置权限并包含有效值,密钥值存储对象将其数据写入用户的iCloud帐户中的相应位置。如果附加到指定的iCloud容器有问题,任何尝试读取或写入键值将失败。为了确保键值存储配置正确可访问,您应该在应用程序的启动周期早期执行类似于以下代码:

NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
          selector:@selector(updateKVStoreItems:)
          name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
          object:store];
[store synchronize];

建议您在应用程序启动周期的早期创建键值存储对象,因为它可以确保您的应用程序及时从iCloud接收更新。确定对键和值进行更改的最佳方法是注册通知NSUbiquitousKeyValueStoreDidChangeExternalNotification。在启动时,您应该手动调用synchronized方法来检测是否从外部进行了任何更改。在应用程序执行期间,您无需在其他时间调用该方法。

有关如何配置iOS应用程序的权利的详细信息,请参阅在App Distribution Guide中添加功能。

访问键值存储中的值

您可以使用NSUbiquitousKeyValueStore类的方法获取并设置键值存储值。此类具有获取和设置类型为Boolean,long long和double的标量值的首选项的方法。它还具有获取和设置值为NSData,NSDate,NSString,NSNumber,NSArray或NSDictionary对象的键的方法。

如果您使用键值存储作为更新本地存储的首选项的方式,则可以使用与清单3-1相似的代码来协调用户默认数据库的更新。此示例假定您在iCloud和用户默认数据库中使用相同的键名称和相应的值。它还假定您以前注册了updateKVStoreItems:方法作为响应通知NSUbiquitousKeyValueStoreDidChangeExternalNotification的方法。

清单3-1使用iCloud更新本地偏好值

- (void)updateKVStoreItems:(NSNotification*)notification {
   // Get the list of keys that changed.
   NSDictionary* userInfo = [notification userInfo];
   NSNumber* reasonForChange = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey];
   NSInteger reason = -1;

   // If a reason could not be determined, do not update anything.
   if (!reasonForChange)
      return;

   // Update only for changes from the server.
   reason = [reasonForChange integerValue];
   if ((reason == NSUbiquitousKeyValueStoreServerChange) ||
         (reason == NSUbiquitousKeyValueStoreInitialSyncChange)) {
      // If something is changing externally, get the changes
      // and update the corresponding keys locally.
      NSArray* changedKeys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
      NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];

      // This loop assumes you are using the same key names in both
      // the user defaults database and the iCloud key-value store
      for (NSString* key in changedKeys) {
         id value = [store objectForKey:key];
         [userDefaults setObject:value forKey:key];
      }
   }
}

定义关键价值商店变更的范围

对NSUbiquitousKeyValueStore方法之一的每次调用都被视为一个单一的原子事务。将该事务的数据传输到iCloud时,整个事务将失败或成功。如果成功,所有的密钥都被写入存储,如果没有写入密钥,则它们将失败。商店没有部分写钥匙。发生故障时,系统还会生成包含失败原因的NSUbiquitousKeyValueStoreDidChangeExternalNotification通知。如果您正在使用键值存储,则应使用该通知来检测可能出现的问题。

如果您有一组键值必须同时更新,才能生效,请将它们一起存储在一个事务中。要在单个事务中写入多个键和值,请创建具有所有键和值的NSDictionary对象。然后使用setDictionary:forKey:方法将字典对象写入键值存储。编写整个变更字典可确保所有的键都被写入,或者没有一个。

实施iOS设置软件包

在iOS中,Foundation框架提供了存储偏好数据的低级机制。应用程序有两个选项可用于显示首选项:

显示应用程式内的偏好设定
使用“设置”软件包可以从“设置”应用程序管理首选项。
您选择哪个选项取决于用户如何与首选项进行交互。 “设置”包通常是显示首选项的首选机制。但是,包含配置选项或其他频繁访问的首选项的游戏和其他应用程序可能希望将其显示在应用程序内。无论您如何呈现它们,您都可以使用NSUserDefaults类来访问代码中的偏好值。

本章重点介绍为您的应用创建一个“设置”包。 “设置”包包含描述您的首选项的结构和演示样式的文件。 “设置”应用程序使用此信息为应用程序创建一个条目,并显示自定义首选项页面。

有关如何管理和呈现设置和配置选项的指导,请参阅iOS人机界面指南。

设置应用界面

“设置”应用程序实现一组分层的页面,用于导航应用程序首选项。 “设置”应用的主页面列出了可以自定义其首选项的系统和第三方应用程序。选择第三方应用程序会将用户置于该应用的首选项。

每个具有“设置”包的应用程序至少有一页首选项,称为主页面。如果你的应用程序只有几个首选项,主页面可能是唯一需要的。但是,如果首选项数量太大而不适合主页,则可以创建链接主页面或其他子页面的子页面。您可以创建的子页面数量没有特定的限制,但是您应该努力使您的首选项尽可能简单和易于导航。

每个页面的内容由您配置的一个或多个控件组成。表4-1列出了“设置”应用程序支持的控件类型,并介绍了如何使用每种类型。该表还列出了存储在“设置”包的配置文件中的原始密钥名称。

表4-1偏好控制类型

控制类型
描述
文本域
文本字段类型显示标题(可选)和可编辑的文本字段。您可以将此类型用于需要用户指定自定义字符串值的首选项。
这种类型的键是PSTextFieldSpecifier。
标题
标题类型显示只读字符串值。您可以使用此类型显示只读首选项值。 (如果偏好包含隐藏或非直观的值,则此类型可以将可能的值映射到自定义字符串。)
这种类型的键是PSTitleValueSpecifier。
拨动开关
拨动开关类型显示ON / OFF开关按钮。您可以使用此类型配置只能有两个值之一的首选项。虽然您通常使用此类型来表示包含布尔值的首选项,但您也可以使用包含非布尔值的首选项。
此类型的键是PSToggleSwitchSpecifier。
滑块
滑块类型显示滑块控件。您可以使用此类型表示一系列值的首选项。此类型的值是您指定的最小值和最大值的实数。
这种类型的关键是PSSliderSpecifier。
多值
多值类型允许用户从值列表中选择一个值。您可以将此类型用于支持一组互斥值的首选项。值可以是任何类型。
此类型的关键是PSMultiValueSpecifier。

组类型用于在单个页面上组织偏好组。组类型不表示可配置的首选项。它只包含在一个或多个可配置首选项之前显示的标题字符串。
这种类型的关键是PSGroupSpecifier。
儿童窗格
子窗格类型允许用户导航到新页面的首选项。您可以使用此类型实现分层首选项。有关如何配置和使用此首选项类型的更多信息,请参阅分层首选项。
这种类型的关键是PSChildPaneSpecifier。

有关每种首选项类型格式的详细信息,请参阅“设置应用程序架构参考”。 要了解如何创建和编辑设置页面文件,请参阅创建和修改设置包。

设置包

“设置”包的名称为Settings.bundle,位于应用程序捆绑包的顶级目录中。 此软件包包含一个或多个描述偏好设置页面的“设置”页面文件。 它还可能包括显示您的首选项所需的其他支持文件,例如图像或本地化字符串。 典型的“设置”包的内容如表4-2所示。

表4-2 Settings.bundle目录的内容

项目名
描述
Root.plist
“设置”页面文件包含根页面的首选项。该文件的名称必须是Root.plist。该文件的内容在“设置页面文件格式”中有更详细的描述。
其他.plist文件
如果您使用子窗格构建一组分层首选项,则每个子窗格的内容将存储在单独的“设置”页面文件中。您负责命名这些文件并将其与正确的子窗格相关联。
一个或多个.lproj目录
这些目录为“设置”页面文件存储本地化的字符串资源每个目录包含单个字符串文件,其标题在“设置”页面文件中指定。字符串文件提供本地化字符串以显示您的首选项。
其他图像
如果使用滑块控件,则可以将滑块的图像存储在软件包的顶级目录中。
除了设置包之外,应用包可以包含应用设置的自定义图标。 “设置”应用程序会在应用程序首选项的条目旁边显示您提供的图标。有关应用程序图标的信息以及如何指定它们,请参阅“iOS编程指南”。

当设置应用启动时,它会检查每个自定义应用程序是否存在“设置”包。对于找到的每个自定义捆绑包,它加载该捆绑包,并在“设置”主页面中显示相应的应用程序的名称和图标。当用户点击属于您的应用程序的行时,“设置”将加载“设置”包的Root.plist设置页面文件,并使用该文件构建应用程序的首选项。

除了加载您的软件包的Root.plist设置页面文件之外,“设置”应用程序还会根据需要加载该文件的任何特定于语言的资源。每个“设置”页面文件都可以包含一个关联的.strings文件,其中包含任何用户可见字符串的本地化值。当您准备显示的偏好设置时,“设置”应用程序会以用户首选语言查找字符串资源,并在显示之前将其替换为您的首选项页面。

设置页面文件格式
每个“设置”页面文件都以iPhone设置属性列表文件格式存储,这是一种结构化文件格式。编辑“设置”页面文件的最简单的方法是使用Xcode的内置编辑器设施;请参阅准备设置页面进行编辑。您还可以使用Xcode工具附带的“属性列表编辑器”应用程序编辑属性列表文件。

注意:在构建应用程序时,Xcode会将您项目中的任何基于XML的属性文件转换为二进制格式。此转换可以节省空间,并为您自动完成。
每个“设置”页面文件的根元素包含表4-3中列出的键。实际上只需要一个键,但建议您包含这两个键。

表4-3首选项的根级别键设置页面文件

类型

首选项(必需)
排列
该键的值是一个字典数组,每个字典包含单个控件的信息。有关控制类型的列表,请参见表4-1。有关与每个控件相关联的键的说明,请参阅设置应用程序架构参考。
StringsTable

与此文件关联的字符串文件的名称。该文件的副本(具有适当的本地化字符串)应位于每个包的特定于语言的项目目录中。如果不包含此键,则该文件中的字符串不会本地化。有关如何使用这些字符串的信息,请参阅本地化资源。
层次偏好
如果您打算分层组织您的首选项,则您定义的每个页面都必须有自己独立的.plist文件。每个.plist文件都包含仅在该页面上显示的一组首选项。您的应用程序的主要首选项页面始终存储在名为Root.plist的文件中。额外的页面可以给你任何你喜欢的名字。

要指定父页面和子页面之间的链接,您需要在父页面中包含子窗格控件。子窗格控件创建一个行,当点击时,会显示一个新的设置页面。子窗格控件的“文件”键标识带有子页面内容的.plist文件的名称。标题键标识子页面的标题;此标题也用作用于显示子页面的控件的文本。 “设置”应用程序自动提供子页面上的导航控件,以允许用户返回到父页面。

图4-1显示了这种分层的页面集合是如何工作的。该图的左侧显示.plist文件,右侧显示相应页面之间的关系。

图4-1使用子窗格组织首选项

有关子窗格控件及其关联键的详细信息,请参阅设置应用程序架构参考。

本地化资源
因为首选项包含用户可见的字符串,所以您应该使用设置包提供这些字符串的本地化版本。每个首选项可以为您的捆绑包支持的每个本地区拥有一个关联的.strings文件。当“设置”应用程序遇到支持本地化的密钥时,它会检查适当本地化的.strings文件以获取匹配的密钥。如果找到一个,它将显示与该关键字关联的值。

在查找.strings文件等本地化资源时,“设置”应用程序遵循与其他iOS应用程序相同的规则。它首先尝试查找与用户首选语言设置匹配的资源的本地化版本。如果没有这样的资源,则选择适当的后备语言。

有关字符串文件格式,特定于语言的项目目录以及从捆绑包中检索特定语言资源的信息,请参阅“国际化和本地化指南”。

创建和修改设置包

Xcode提供了一个模板,用于将当前项目添加到“设置”包中。默认的“设置”包包含一个Root.plist文件和用于存储任何本地化资源的默认语言目录。您可以根据需要扩展此捆绑包,以包含“设置”包所需的其他属性列表文件和资源。

添加设置包
要将设置包添加到您的Xcode项目中:

选择文件>新建>新建文件。
在iOS下,选择资源,然后选择设置包模板。
将文件命名为Settings.bundle。
除了向项目添加新的“设置”包之外,Xcode还会将该捆绑包添加到应用程序目标的“复制捆绑资源”构建阶段。因此,您只需修改“设置”包的属性列表文件,并添加任何所需的资源。

新的“设置”包具有以下结构:

Settings.bundle/
Root.plist
en.lproj/
Root.strings

准备编辑设置页面
在编辑设置包中的任何属性列表文件之前,您应该配置Xcode编辑器,以将这些文件的内容格式化为iPhone设置。 Xcode会自动为Root.plist文件执行此操作,但您可能需要手动格式化其他属性列表文件。 要将文件格式化为iPhone设置,请执行以下操作:

选择文件。
按住Control键并单击编辑器窗口,如果尚未选择,请选择“属性列表类型”>“iPhone设置”。
格式化属性列表可以更容易地理解和编辑文件的内容。 Xcode代替适合所选格式的人类可读字符串(如图4-2所示)。

图4-2 Root.plist文件的格式化内容

配置设置页面:教程
本节介绍如何配置“设置”页面以显示所需的控件。 本教程的目标是创建一个如图4-3所示的页面。 如果尚未为项目创建“设置”包,则应按照添加设置包中的说明进行操作,然后再继续执行这些步骤。

图4-3“根设置”页面

打开“首选项”键以显示模板附带的默认项。
将项目0的标题更改为声音。
披露优先项目0。
将“标题”键的值从“组”更改为“声音”。
将“类型”键设置为“组”。
单击项目的公开三角形以隐藏其内容。
为重命名的Sound组创建第一个切换开关。
选择首选项目的项目2(切换开关项目),然后选择编辑>剪切。
选择项目0,然后选择编辑>粘贴。 (这将移动切换开关项目在文本字段项目前面。)
打开切换开关项目以显示其配置键。
将标题键的值更改为播放声音。
将Identifier键的值更改为play_sounds_preference。
单击项目的公开三角形以隐藏其内容。
为“声音”组创建第二个拨动开关。
选择项目1(播放声音切换开关)。
选择编辑>复制。
选择“编辑”>“粘贴”,将第一个切换开关的副本放在第一个。
打开新的切换开关项目以显示其配置键。
将其标题键的值更改为3D Sound。
将其Identifier键的值更改为3D_sound_preference。
单击项目的公开三角形以隐藏其内容。
此时,您已完成第一组设置,并可以创建用户信息组。
将项目3更改为组控件,并将其命名为用户信息。
单击首选项中的项目3。这将显示一个包含项目类型列表的弹出式菜单。
从弹出菜单中,选择组以更改控件的类型。
披露项目3的内容。
将“标题”键的值设置为“用户信息”。
单击项目的公开三角形以隐藏其内容。
创建名称字段。
在首选项中选择项目4。
使用弹出菜单将其类型更改为文本字段。
将“标题”键的值设置为“名称”。
将Identifier键的值设置为user_name。
单击项目的公开三角形以隐藏其内容。
创建体验级设置。
选择项目4。
按住Control键并单击编辑器窗口,然后选择添加行添加新项目。
将新项目的类型设置为多值。
披露项目的内容,并将其标题设置为体验级别,其标识符为experience_preference,其默认值为0。
选择“默认值”键后,按住Control键并单击并添加行添加标题数组。
选择“标题”数组,然后按“返回”添加新的子项。
再添加两个子项,共创建三个项目。
将子项的值设置为“初学者”,“专家”和“主”。
隐藏密钥的子项。
为Values数组添加一个新项目。
将三个子项添加到Values数组,并将其值设置为0,1和2。
隐藏项目5的内容。
将最后一个组添加到您的设置页面。
创建一个新项目并将其类型设置为“组”,并将其标题设置为“重力”。
创建另一个新项目并将其类型设置为Slider,其标识符为gravity_preference,其默认值为1,其最大值设置为2。

创建其他设置页面文件
设置包模板包括Root.plist文件,它定义了您的应用程序的顶部“设置”页面。要定义其他“设置”页面,您必须将其他属性列表文件添加到“设置”包中。

要将属性列表文件添加到Xcode中的“设置”包中,请执行以下操作:

选择文件>新建>新建文件。
在iOS下,选择资源,然后选择属性列表模板。
选择新文件以在编辑器中显示其内容。
按住Control键并单击编辑器窗格,然后选择“属性列表类型”>“iPhone设置”,以格式化内容。
按住Control键再次单击编辑器窗格,然后选择添加行添加新的键。
添加和配置您需要的任何其他键。
将新的“设置”页面添加到“设置”包后,可以按照“配置设置页面:教程”中所述编辑页面的内容。要显示页面的设置,您必须从子窗格控件中引用它,如“分层首选项”中所述。

注意:在Xcode 4中,向项目添加属性列表文件不会自动将其与您的“设置”包相关联。您必须使用Finder将任何其他属性列表文件移动到“设置”包中。
模拟应用程序的调试首选项

运行应用程序时,iOS Simulator会将您应用程序的任何首选项存储在〜/ Library / Application Support / iOS Simulator / User / Applications / <APP_ID> / Library / Preferences中,其中<APP_ID>是iOS使用的以编程方式生成的目录名识别您的应用程序

每次构建应用程序时,Xcode会保留您的应用程序首选项和其他相关的库文件。如果要删除当前的首选项进行测试,可以从Simulator中删除应用程序,或从“iOS模拟器”菜单中选择“重置内容和设置”。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容