iPhone开发基础教程笔记(十)--第十章 应用程序设置和用户默认设置

第十章 应用程序设置和用户默认设置
iPhone有一个专用的Settings应用程序,你一定已经使用过多次了。本章将介绍如何在Settings应用程序中添加设置,以及如何从应用程序内部访问这些设置。

苹果公司关于Settings的属于及其底层机制,可以在Application Preferences中找到。

10.1 了解设置束
通过Settings应用程序,用户可以输入和更改任何带有设置束(Settings bundle)的应用程序的首选项。设置束是构建到应用程序的一组文件,她告诉Settings应用程序,主应用程序希望从用户那里手机到哪些首选项。
在iPhone的用户默认设置机制下,Settings应用程序充当着一个通用的用户界面。用户默认设置是应用程序首选项的一部分,由NSUserDefaults类实现,用于保存和获取首选项。应用程序通过NSUserDefaults使用键值读取和保存首选项数据。不同之处在于NSUserDefaults数据被持久化到文件系统中,而没有存储在内存中的对象实例中。
本章将创建一个应用程序,添加并配置一个设置束,然后从应用程序中访问并编辑这些首选项。
Settings应用程序的优势之一是无需为首选项设计用户界面。创建属性列表来定约应用程序的可用设置后,Settings应用程序会自动创建用户界面。但是,使用Settings应用程序也有一些限制。当应用程序正在运行时,用户可能需要更改的任何首选项都不应该受到Settings应用程序的限制,因为用户可能被强制退出应用程序以更改这些值。

互动式应用程序,如游戏,通常应该提供首选项视图,使用户更改设置时无需退出应用程序。甚至实用工具和生产应用程序的首选项,也应该能够让用户在不离开应用程序的情况下进行更改。

10.2 AppSettings应用程序
接下来,我们将创建一个简单的应用程序。首先实现一个设置束,这样,当用户启动Settings应用程序时,其中将包含我们的应用程序的一个条目。
如果用户选择我们的有粗有细,他将看到一个视图,其中显示与我们的应用程序相关的首选项。
此视图中有两项包含显示指示符。第一个是Protocol,他导向另一个表视图,其中显示该项的可用选项。用户只能在表视图中选择一个值。
在应用程序的Settings应用程序的主视图还有一个显示指示符,那就是MoreSettings,他将用户引导至另一组首选项。该子视图可能与主视图拥有相同类型的控件,它还可以有自己的子视图。Settings应用程序需要使用导航控制器,这是因为他支持构建多级首选项视图。
实际启动AppSettings应用程序后,将会显示一组从Settings应用程序收集来的首选项。
为了介绍如何更新应用程序的首选项,我们还在右下角提供了一个较小的信息按钮,他将用户引导至另一个视图,以设置应用程序的两个首选项值。

10.3 创建项目
新建项目,左侧窗格选择iPhone标题下的Application,单击Utility Application图标,命名为AppSettings。
我们先熟悉一下这个新模板。该模版创建的应用程序与第六章创建的应用程序非常相似。此应用程序有一个主视图和一个辅助视图(称为flipside视图)。点击主视图中的信息按钮将进入flipside视图,点击flipside视图中的Done按钮将返回主视图。
你将注意到,Xcode项目中没有Classes文件夹。因为实现这种类型的有粗有细需要许多文件,该模版已经将这些文件组织到一些分组中。展开文件夹MainView、Flipside View和Application Controllers,然后将Resources也打开。
组成主视图的所有类,包括视图控制器和一个UIView子类,都包含在MainView文件夹中。同样,实线Flipside视图所需的所有源代码都包含在FlipsideView文件夹中。最后,应用程序委托和根控制器类都包含在Application Controllers文件夹中。
此模版为主视图和Flipside视图提供了一个自定义的UIView子类。实际上,在此应用程序中,主视图和Flipside视图都不需要UIView子类,但是我们会将FlipsideView和MainView保留在项目中。保留他们并没有什么坏处,但如果删除他们,我们就必须重新连接nib文件。使其指向UIView。
接下来快速更改一下MainWindow.xib。打开MainWindow.xib,将主窗口设置为列表模式(中间的View Mode按钮)。然后,单击Root View Controller图标左侧的显示三角符号,显示View图标。接下来单击View图标左侧的显示三角形,显示Light Info Button图标。(可以按Option键并单击Root View Controller左侧的三角形,会展开所有层次)
接下来,更改Light Info Button图标,让他在白色背景中显得更为美观。单击Light Info Button,按Cmd+1打开属性检查器,将按钮的Type由Info Light改为Info Dark。
同样,所需图标从本书附带的10 AppSettings中抓取icon.png添加到Resources文件夹。
然后,单击Resources文件夹中的info.plist文件,将Icon文件行的值设置为icon.png。

10.4 使用设置束
Settings应用程序根据给定应用程序内部的设置束内容来显示该应用程序的首选项。每个设置束必须包含一个名为Root.plist的属性列表,他定义根级首选项视图。此属性列表必须遵循一种非常精确的格式。如果Settings应用程序发现设置束中包含一个合适的Root.plist文件,他将根据属性列表的内容为应用程序构建一个设置视图。如果想要首选项包含任何子视图,则必须向设置束中添加其他属性列表,并为每个子视图添加一个Root.plist条目。
此过程的一个缺点是,无法在Xcode中为设置束添加或删除项。可以从Xcode中更改设置束中已有文件的内容,但是如果需要真正地添加或删除项,则必须在Finder中完成。

10.4.1 在项目中添加设置束
在Groups&Files窗格中,单击根对象(名为AppSettings,应该位于列表最顶端),File=》New File,选择左侧窗格中iPhone OS标题下的Settings,然后选择Settings Bundle图标,单击Next,单击回车键选择默认名称Settings Bundle。
现在应该在Xcode的Groups&Files窗格中看到一个新项,名为Settings.bundle。展开他,应该看到Root.plist图标和en.lproj文件夹。现在,主要介绍Root.plist,我们将在17章介绍本地化应用程序时讨论en.lproj。

10.4.2 设置属性列表
单击Root.plist,查看编辑器窗格,你将看到Xcode的属性列表编辑器。此编辑器与/Developer/Applications/Utilities中的Property List Editor应用程序的功能相同。
所有属性列表都有一个类型为Dictionary的根节点,该节点使用键值来保存项,与NSDictionary相同。Dictionary节点的所有子节点都必须有一个键和一个值。任何给定的属性列表都只能有一个根节点,并且所有其他节点都必须位于该节点之下。
属性列表可以包含几种不同类型的节点。出Dictionary节点外,还有Array结点。Dictionary和Array是唯一能够包含其他节点的属性列表节点类型。还有其他一些节点类型可以保存数据,如Boolean、Data、Date、Number和String.

提示:在NSDictionary中可以使用任何对象作为键,但属性列表Dictionary节点中的值必须为字符串类型,可以选择任何节点类型做为该键的值。

创建一个设置属性列表时,必须遵循已定义的格式。所幸,当在项目中添加设置束时,应用程序会创建一个具有适当格式的属性列表,名为Root.plist。
在Root.plist编辑器窗格中,展开PreferenceSpecifiers节点。
添加首选项标识符之前,先了解一下属性列表以及要求的格式。Root结点下的第一项是键Title,这个名称将出现在应用程序的Settings应用程序部分中。双击Title旁边的当前值YOUR_PROJECT_NAME,改为AppSettings。
第二项是StringTable,第十七章也会讲到他。还使用了一个字符串表,用于将应用程序翻译为其他语言。由于这一项是可选的,所以你可以单击StringsTable并按delete键删除此条目。他不影响任何操作,因此你也可以保留它,但是如果删除他,表中就只有一项了。
根节点的下一项是PreferenceSpecifiers,他是一个数组。单击其显示三角形可以显示他的子项。这个数组节点用于保存一组Dictionary结点,每个Dictionary结点都代表用户可输入的一个首选项或用户可以访问的一个子视图。你将注意到Xcode的模版有4个节点。这些节点与我们实际的首选项没有任何联系,所以删除Item 2、Item3、Item4。
单击Item1,但不要展开他。注意此行右边带有加号图标的按钮,它用于在这行后面添加一个同级节点。创建一个同级节点Item2。
现在展开Item1,此时加号图标改为一个带有三条横线的图标。此新图标表示单击该按钮将添加一个子节点。
展开Item2,他下发的第一行有一个键Type,PreferenceSpecifiers数组中的每个属性列表节点都必须有一个带有此键的条目。他通常位于第一项的位置,但在Dictionary结点中顺序并不重要,所以Type键无需是第一个键。当前Item1的值PSGroupSpecifier用于表示应该启动一个新组。设置束属性列表中PreferenceSpecifiers数组中的Item1应该始终具有此类型,因为每个表至少需要一个组。
Item1中包含一个名为Title的键,它用于在启动的组上设置一个可选标题。双击Title旁边的值,将其由Group改为General Info。

10.4.3 添加文本字段设置
现在,我们需要在此数组中添加另一个项,他将表示第一个实际的首选项字段。我们将从一个简单的文本字段开始。如果在编辑器窗格中单击PreferenceSpecifiers行,然后单击按钮添加一个子项,新行将被插入到列表的顶端,这不是我们想要的结果。我们希望在数组的末尾添加一个项。谓词,单击Item 1左侧的三角形将其折叠,然后选择Item1,并单击此行末尾的加号按钮,这将在当前行之后添加一个同级的行。
新行默认为String节点类型,这不是我们需要的类型。记住,PreferenceSpecifiers数组中的每一项都必须为Dictionary类型,因此把String改为Dictionary。展开Item2,添加子节点。
有一个新行出现,其默认类型是String,这是我们所需要的类型。新行的键值默认为New Item,将它改为Type,值改为PSTextFieldSpecifier,他告诉Settings应用程序,我们希望用户在文本字段中编辑此设置。
在此例中,PSTextFieldSpecifier是一种类型,更具体地讲,他是一种特定的首选项字段类型。找到Key列中的Type,我们定义将用于编辑首选项的字段的类型。
单击Type行右侧的加号图标按钮,向Dictionary添加另一项。这一行指定将在文本字段旁边显示的标签。将键New Item改为Title,现在按下Tab键,注意,此时要编辑Value列中的值,将他设置为Username。现在按下Title行末尾的加号按钮,向Dictionary添加另一项。
向新条目的键改为Key,将其设为username。这个新条目告诉Settings应用程序,在存储此文本字段中输入的值时使用什么键。Settings应用程序将对为你保存的每个首选项进行同样的操作。如果你为其提供了一个键值foo,则稍后可以在应用程序中请求foo值,他会显示用户为该首选项输入的值。

说明:Title拥有值Username,而Key拥有值username。这种大小写差异将会经常出现。Title是在屏幕上显示的内容,所以使用大写字母“U”比较合适。而Key是一个文本字符串,用于从用户默认设置获取首选项,所以所有字母采用小写形式比较合适。我们可以将Title的内容全部小写吗?当然可以。这只是风格问题。

向Dictionary添加另一项,将其键设置为Autocapitalization,将其值设为None,这指定文本字段不要尝试自动大写用户输入的内容。
创建最后一个新行,将其键设置为AutocorrectionType,将其设置为No。他告诉Settings应用程序不要尝试自动更正输入到该文本字段中的值。否则将其值设为YES。

保存属性文件,检查所有属性是否已正确设置并能够生效。现在我们编译运行应用程序。应该能够在Settings中查看到应用程序的条目了。


10.4.4 添加安全文本字段设置
添加次节点的一个简单方法是:折叠PreferenceSpecifiers数组中的Item 2,然后选择Item2,Cmd+C复制,Cmd+V粘帖,将创建一个与Item2相同的新项Item3.展开Item 3,将Title改为Password,将Key改为password。
接下来,向Item 3中添加一个子项。记住,项的顺序无关紧要,将新项放置在Key项目下方即可,将其Key设置为IsSecure,将其Type改为Boolean。完成这些操作后,平常用于输入值的空间将变成一个复选框。单击复选框将其选中,这告诉Settings应用程序此字段应该是一个密码字段,而非普通的文本字段。

10.4.5  添加多值字段
我们将添加的下一项是一个多值字段。这种字段类型会自动生成带有显示指示符的行。单击显示指示符将进入另一个表,你可以在多行中进行选择。
添加Item 4,将Item 4的Type改为Dictionary,然后展开Item 4。
向Item 4中添加3个子行。每一行的键和值分别为:Type和PSMultiValueSpecifier、Title和Protocol、Key和protocol。接下来的操作有些麻烦。
我们将向Item 4中添加另外两个子项,但他们的节点类型是Array,而不是String。其中一项名为Titles,用于保存可供用户选择的一组值。另一项是Values,用于保存用户默认设置中实际存储的一组值。Values列表中的第一项与Titles数组中的第一项相对应。因此,如果用户选择第一项,Settings应用程序实际保存的是Values数组中的第一个值。这种Titles/Values对非常方便,为用户提供了易于理解的文本,而实际保存的确实其他文本,如数字、日期、或不同的字符串。这两个数组都是必须的。如果希望两个数组的内容相同,可以只创建一个数组,然后进行复制黏贴即可。
向Item 4中添加一个新的子项,将其键改为Values,并将其类型设置为Array。展开数组,添加5个子节点,所有节点都是String类型且包括以下值:HTTP、SMTP、NNTP、IMAP和POP3。

提示:如果输入第一个值并按下回车键,将会编辑他下面的值,这是一种快捷方式。

输入上述5个值后,复制粘帖,并重命名为Titles。
还少一个默认值。再添加一个子项,键为DefaultValue,值为SMTP。

10.4.6 添加拨动开关设置
添加Item 5,并为其添加3个子行。每行的键和值分布为:Type和PSToggleSwitchSpecifier、Title和Warp Drive、Key和warp。
默认情况下,拨动开关产生一个Boolean值YES或NO,该值将保存到用户默认设置中。如果想在打开和关闭位置上指定不同的值,可以指定可选的键TrueValue或FalseValue。你可以在打开位置(TrueValue)或关闭位置(FalseValue)指定字符串、日期或数字,这样,Settings应用程序就会保存你指定的字符串,而不是保存YES或NO。我们将打开位置设置为保存字符串Engaged,将关闭位置设置为保存字符串Disabled。即再添加两个子项,其键和值分别为:TrueValue和Engaged、FalseValue和Disabled。
还需要设置一个默认值。如果我们没有提供FalseValue和TrueValue,则需要创建一个新行,将其键设置为DefaultValue,并将其类型改为Boolean。但是,由于我们确实已经添加了这两项,所以我们输入到DefaultValue中的值必须与传入TrueValue或FalseValue的值相匹配。即添加一个键值为DefaultValue 值为Engaged的新子项。

10.4.7 添加滑块设置
接下来我们将添加的项是一个滑块。在Settings应用程序中,滑块两段可以各有一个小图像,但他不能有标签。我们将滑块放置在一个带有自己的标题的组中,以便用户了解滑块的作用。
单击PreferenceSpecifiers下的Item1,复制。现在选择Item 5,同时确保他为折叠状态,粘帖。由于Item 1是一个组标识符,所以我们刚才粘帖进来的新项Item 6也是一个组标识符,他将告诉Settings应用程序,应该在此位置生成一个新组。
展开Item 6,更改键Title的值为Warp Factor。
折叠Item 6,将其选中,添加新的同级行Item 7.向Item 7添加两个子行,键Type值PSSliderSpecifier,键Key和warpfacotor。
我们允许用户输入一个1到10之间的值,默认值为warp 5.滑块需要一个最小值和一个最大值和一个起始值。所有这些值都是数字。谓词,向Item 7添加3个子行,DefaultValue=5 MinimumValue=1 MaximumValue=10
滑块两端允许放置21*21像素的小图像。我们将提供一些小图标来指示向左滑动会降低速度,向右滑动会提高速度。
从项目归档文件10 AppSettings文件夹中找到rabbit.png和turtle.png。因为Settings应用程序需要使用这两个图标,因此不能仅将他们放在Resources文件夹中,而需要将其放在设置束中,这样Settings应用程序才能获取他们。为此,进入Finder并导航到存储Xcode项目的文件夹。这个文件夹包含一个名为Settings.bundle的图标。
记住,在Finder中,束看起来像文件,但实际上是文件夹,可以右键单击束的图标,然后选择Show Package Contents来访问束的内容。你应该能够看到在Xcode的Settings.bundle中看到的两个图标。将图标文件rabbit.png和turtle.png复制到此。
将刚才复制到设置束中的两个文件添加到Xcode项目的Resources文件夹中。无需将他们复制到项目中,只需创建首选项即可。这使我们能够在应用程序中使用这两个图标。即使设置束中的图像将被编译到应用程序中,他们也不能被我们的应用程序访问,只能供Settings应用程序访问,除非我们执行了这一步。
在Finder中将此次看保留为打开状态,因为稍后我们还要在其中复制另一个文件。现在,返回Xcode,告诉滑块使用这两个图像。
返回到Root.plist,在Item 7下添加两个子行,将他们的键和值分别设为MinimumValueImage和turtle.png、MaximumValueImage和rabbit.png。

10.4.8 添加子设置视图
在添加这个节点之前,先复制Item 1并粘帖为Item 8,设置键Title的值为Additional Info。
然后创建新项 Item 9.向Item 9添加子行:键Type=PSChildPaneSpecifier 键Title=More Settings
我们需要添加最后一行,他将告诉Settings应用程序,为More Settings视图加载哪个属性列表。键File=More。假定文件扩展名为.plist,且不应该包含在文件名中,否则Settings应用程序将找不到此属性列表文件。
现在,我们需要向设置束中添加另一个属性列表,以描述应该在子视图上获取的首选项值。Settings应用程序中的每个视图都以一个属性列表文件为基础,因此我们需要向设置束中添加另一个属性列表文件来定约该子视图。不能在Xcode中向设置束添加新文件,且属性列表编辑器的Save对话框不允许将新文件保存到设置束中。因此,我们必须创建一个新的属性列表,将它保存在其他地方,然后使用Finder将它拖入Settings.bundle窗口。
从项目归档文件10 AppSettings文件夹中找到More.plist,将它复制到Settings.bundle窗口。这样,我们就能够将此新属性列表添加到Root.plist文件的子项。

提示:创建子设置视图的最简单的方法是复制Root.plist,并重命名,然后删除第一项以外的所有现有首选项标识符,将需要的任何首选项标识符添加到此新文件中。

你可以在iPhone开发中心的Settings Application Schema Reference文档中找到设置属性列表格式的完整文档。
http://developer.apple.com/iphone/library/navigation/Reference.html

10.5 读取应用程序中的设置
我们将使用NSUserDefaults类读取用户设置。NSUserDefaults做为单一实例实现,这意味着应用程序中只有一个NSUserDefaults实例在运行。
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
在创建应用程序的属性列表时,将创建一个PreferenceSpecifiers数组,其中一些标识符用于创建组。另一些标识符用于创建用户进行设置时使用的接口对象。这些才是我们真正感兴趣的标识符,因为他们才是实际数据所在的地方。绑定到一个用户设置的每个标识符都有一个名为Key的值。回顾一下前面的内容,例如,滑块的键拥有值warpfacotor。Password的键为password。
现在我们已经拥有了显示设置的位置,接下来使用一组标签快速设置一下主视图。进入IB之前,先为我们需要的所有标签创建输出口。
单击MainViewController.h:

#define kUsernameKey @"username"
#define kPasswordKey @"password"
#define kProtocolKey @"protocol"
...

#import <UIKit/UIKit.h>

@interface MainViewController:UIViewController {
 IBOutlet UILabel *usernameLabel;
 IBOutlet UILabel *passwordLabel;
 IBOutlet UILabel *protocolLabel;
 ...
}
@property ...
- (void)refreshFields;
@end

双击MainView.xib,视图出现后,他的背景为暗灰色,将其改为白色。在nib的主窗口中单击MainView,cmd+1打开属性检查器,使用Background将其改为白色。从库中拖出20个标签,一半是静态的,他们为粗体且右对齐;另一半用于显示用户默认设置获取的实际值,并使输出口指向这些标签。

单击MainViewController.m:

...
- (void)refreshFields
{ NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
  usernameLabel.text=[defaults objectForKey:kUsernameKey];
  ...
}

- (void)viewDidAppear:(BOOL)animated {
 [self refreshFields];
 [super viewDidAppear:animated];
}
- (void)viewDidLoad {
 [self refreshFields];
}

...
@end

我们添加viewDidLoad和viewDidAppear:方法,在其中调用refreshFields方法。(这里为什么要用这两个方法?难道DidLoad的时候不调用DidAppear方法?)
还有我们为什么选择viewDidAppear而不是viewWillAppear方法?原因是我们想在将视图呈现给用户之前进行更改。具体原理涉及到时限。我们将在FlipsideViewController的viewWillDisappear:方法中将更改返回到用户默认设置中。该控制器中的viewWillAppear方法将在FlipsideViewController控制器的viewWillDisappear方法之前被调用,因此对字段的更新不会生效。但是不用担心,因为滑动动画在1s之后才会发生,当主要的辅助试图再次显示时,更新已经完成,用户不会看到更改过程。

完成这个类之后, 现在应该能够编译并运行应用程序了。

10.6 更改应用程序中的默认设置

接下来创建flipside视图。flipside视图具有拨动开关和滑块。
首先,我们需要声明输出口,修改FlipsideViewController.h:

IBOutlet UISwitch *engineSwitch;
IBOutlet UISlider *warpFactorSlider;

现在,双击FlipsideView.xib:打开其中的Flipside View图标。从库中拖出两个Label,并将他们放置到Flipside View窗口。标题改为Warp Engines:和Warp Factor:
接下来,从库中拖出一个Switch,设置位置和输出口,拖出一个Slider,设置位置和输出口,设置其最小值、最大值和初始值等

接下来,修改FlipsideViewController.m:

- (void) viewDidLoad {
 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
 engineSwitch.on=([[defaults objectForKey:kWarpDriveKey] isEqualToString:@"Engaged"])?YES:NO;
 warpFactorSlider.value=[defaults floatForKey:kWarpFactorKey];
}

- (void)viewWillDisappear:(BOOL)animated
{
 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
 NSString *prefValue=(engineSwitch.on)?@"Engaged":@"Disabled";
 [defaults setObject:prefValue forKey:kWarpDriveKey];
 [defaults setFloat:warpFactorSlider.value forKey:kWarpFactorKey];
 [super viewWillDisappear:animted];
}
...
@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值