翻译:iOS视图控制器编程指南(七)——保存和恢复状态(Preserving and Restoring State)

视图控制器在保存和恢复过程中起着非常重要的作用。状态保存记录应用中断前的配置,在随后应用启动恢复配置。将应用恢复到先前的配置可以为用户节省时间,并提供一个更好的用户体验。

保存和恢复过程是自动的,但你需要告诉iOS,需要保留应用的哪些部分。保存应用视图控制器的步骤如下:

· (必须)分配恢复标示符到你想保存的视图控制器上,参见标记保存视图控制器( Tagging View Controllers for Preservation

· (必须)告诉iOS如何在启动时创建或查找新视图控制器对象,参见启动时恢复视图控制器( Restoring View Controllers at Launch Time)。

· (可选)每个视图控制器,存储任何特定配置数据使视图控制器返回到原来的配置,参见视图控制器状态的编码和解码(Encoding and Decoding Your View Controller’s State)。

保存和恢复的过程的概述,参见iOS应用编程指南(App Programming Guide for iOS)。

标记保存视图控制器

UIKit只保存你告诉它要保存的视图控制器。每个视图控制器有一个restorationIdentifier属性,这个属性的默认值为nil。设置这个属性值为有效字符串,告诉UIKit视图控制器和视图应该要保存。可以以编程的方式或者在storyboard文件中设置恢复标示符。

当分配恢复标示符时,在视图层级结构中的所有的父视图控制器必须有恢复标示符。在保存过程中,UIKit从窗口的根视图控制器开始并遍历视图控制器层级结构。如果该层级结构中一个视图控制器没有恢复标示符,该视图控制器和其所有子视图控制器和present的视图控制器都会被忽略。

选择有效的恢复标示符

UIKit使用恢复标示符字符串在之后重新创建视图控制器,所以必须选择代码容易识别的字符串。如果UIKit不能自动创建一个视图控制器,要求你创建,UIKit会提供视图控制器和其所有父视图控制器的恢复标示符。这一串的标示符代表视图控制器的恢复路径,及如何决定哪些视图控制器被请求。恢复路径从根视图控制器开始,包括呈现的每个视图控制器和被请求的视图控制器。

恢复标示图通常以视图控制器类名命名。如果你在很多地方使用相同的类,你可能希望分配更有意义的值。例如,你可以基于视图控制器管理的数据来指定字符串。

每个视图控制器的恢复路径必须是唯一的。如果一个容器视图控制器有两个子视图,容器必须为每个子视图控制器分配一个独特的恢复标示符。一些UIKit中容器视图控制器会自动消除歧义子视图控制器,允许为每个子视图控制器使用相同恢复标示符。例如,类根据每个子视图控制器在导航堆栈中的位置添加信息。关于一个给定视图控制器的行为更多信息,参见相应的类引用。

如何使用恢复标示符和恢复路径创建视图控制器的更多信息,参见启动时恢复视图控制器(Restoring View Controllers at Launch Time)。

不包括视图控制器组

在恢复过程中不包括整个视图控制器组,设置父视图控制器的恢复标示符为nil。图7-1展示了设置恢复标示符为nil对视图层级结构的影响。缺少保存数据,在稍后无法恢复视图控制器。

图7-1 自动保存过程中不包括的视图控制器

在随后的恢复过程中,并不完全删除其中不包括的一个或多个视图控制器。在启动时,任何视图控制器都是应用的一部分,默认创建设置,如图7-2所示。按照默认配置创建视图控制器。

图7-2 加载默认视图控制器组

自动保存过程中,可以手动保存不包括的视图控制器。保存视图控制器的引用即保存视图控制器和其状态信息。例如,如果图7-1中的应用代理保存导航控制器的三个子视图控制器,他们的状态将被保存下来。在恢复过程中,应用代理可以重现创建这些视图控制器并push他们到导航控制器的堆栈上。

保存视图控制器的视图

一些视图有更多与视图而不是父视图控制器相关的状态信息。例如,你希望保存滚动视图的一个滚动位置。视图控制器负责提供滚动视图的内容,而滚动视图本身负责保存其视觉状态。

保存视图状态,执行以下操作:

· 指定为 restoration Identifier 属性一个有效的字符串。

· 使用具有有效标示符的视图控制器的视图。

· 对于表视图和集合视图,指定采用UI Data Source Model Association 协议的数据源

将标示符分配给视图告诉UIKit应该保存视图状态。当稍后恢复视图控制器,UIKit也恢复有恢复标示符的视图的状态。

在启动时恢复视图控制器

在启动时,UIKit试图恢复你的应用到之前的状态。那时,UIKit访问你的应用创建(或确定)用户界面保存的视图控制器对象。当定位视图控制器时,UIKit按照以下顺序搜索:

  1. **如果视图控制器有一个恢复类,UIKit访问该类提供视图控制器。UIKit调用恢复类的 [viewControllerWithRestorationIdentifierPath:coder:](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIViewControllerRestoration_protocol/index.html#//apple_ref/occ/intfcm/UIViewControllerRestoration/viewControllerWithRestorationIdentifierPath:coder:) 方法来检索视图控制器。**如果该方法返回nil,则假设应用并不希望重新创建视图控制器,UIKit停止寻找该视图控制器。
    
  2. **如果视图控制器没有恢复类,UIKit要求应用代理提供视图控制器。**UIKit调用应用代理的[application:viewControllerWithRestorationIdentifierPath:coder:](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:viewControllerWithRestorationIdentifierPath:coder:)方法寻找没有恢复类的视图控制器。如果该方法返回nil,UIKit尝试找到隐藏的视图控制器。
    
  3. **如果有正确恢复路径的视图控制器已经存在,UIKit使用该对象。**如果你的应用在启动时(无论是以编程的方式还是通过storyboard加载的方式)创建视图控制器,并且这些视图控制器有恢复标识符,UIKit基于他们的恢复路径隐式的查找他们。
    
  4. **如果视图控制器最初是从storyboard文件加载,UIKit使用保存的storyboard信息来定位并创建它。**UIKit保存视图控制器的storyboard信息到恢复文件中。在恢复时,UIKit使用这些信息来定位相同的storyboard文件,如果通过其他任何方式没有找到视图控制器,则实例化相应视图控制器。
    

将恢复类分配给视图控制器可以防止UIKit隐式的搜索该视图控制器。使用恢复类让你可以控制是否真正创建一个视图控制器。例如,如果你的类决定不再重新创建视图控制器,viewControllerWithRestorationIdentifierPath:coder:方法可以返回nil。当不存在恢复类,UIKit会尽可能找到或者创建该视图控制器来恢复它。

当使用一个恢复类,viewControllerWithRestorationIdentifierPath:coder: 方法应该创建该类的新实例,执行初始化,并返回结果对象。列表7-1 展示了如何使用此方法从storyboard中加载视图控制器的例子。因为该视图控制器最初从storyboard加载,该方法使用UIStateRestorationViewControllerStoryboardKey key从文件中找到storyboard。注意:该方法不配置视图控制器的数据字段。当解码视图控制器的状态时,发生这一步。

列表7-1 在恢复过程中创建一个新视图控制器

<pre><code>
+(UIViewController*) viewControllerWithRestorationIdentifierPath:(NSArray *) identifierComponents coder:(NSCoder *)coder {

MyViewController* vc;
UIStoryboard* sb = [coder decodeObjectForKey UIStateRestorationViewControllerStoryboardKey];
if (sb) {
    vc = (PushViewController*)[sb instantiateViewControllerWithIdentifier:@"MyViewController"];
    vc.restorationIdentifier = [identifierComponents lastObject];
    vc.restorationClass = [MyViewController class];
}
return vc;

}
</pre></code>

当手动重新创建视图控制器,重新分配恢复标识符和恢复类是个好习惯。恢复恢复标识符的最简单的方法是获取identifierComponents 数组中的最后一项,并将其分配给你的视图控制器。

在启动时由应用主要storyboard文件中创建的对象,不需要为每个对象创建新实例。让UIKit隐式的查找这些对象或者使用应用代理的application:viewControllerWithRestorationIdentifierPath:coder:方法查找这些存在的对象。

编码和解码视图控制器状态

对于每个要保留的对象,UIKit调用对象的encodeRestorableStateWithCoder:方法来保存其状态。在恢复过程中,UIKit调用对应的decodeRestorableStateWithCoder: 方法来解码该状态并将其用于该对象。这些方法的实现是可选的,但是建议,实现。你可能使用它们来保存和恢复以下类型的信息:

· 显示数据的引用(不是数据本身)

· 对于容器视图控制器,其子视图控制器的引用

· 当前选择的信息

· 对于用户可配置视图的视图控制器,当前配置视图的信息

在你的编码和解码方法中,你可以编码对象和编码器支持的任何数据类型。除了视图和视图控制器的其他所有对象必须采用 NSCoding协议并使用协议中的方法来保存其状态。对于视图和视图控制器,编码器不使用 NSCoding协议来保存对象状态。相反,编码器保存该对象的恢复标识符并将其添加到保存对象列表,这将导致该对象的encodeRestorableStateWithCoder: 方法被调用。

视图控制器的encodeRestorableStateWithCoder:decodeRestorableStateWithCoder:方法必须在实现的某个位置调用super方法。调用super方法让父类有机会保存和恢复任何额外的信息。列表7-2展示了这些方法的实现例子,该方法保存一个数值用于识别指定视图控制器。

列表7-2 编码和界面视图控制器状态

<pre><code>
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder {

[super encodeRestorableStateWithCoder:coder];
[coder encodeInt:self.number forKey:MyViewControllerNumber];

}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder {
[super decodeRestorableStateWithCoder:coder];
self.number = [coder decodeIntForKey:MyViewControllerNumber];
}
</pre></code>

在编码和解码过程中,编码对象不共享。每个保存状态的对象接收自己的编码对象。使用独特的编码器表明你不用担心key之间的命名冲突。然而,不要使用UIApplicationStateRestorationBundleVersionKey,UIApplicationStateRestorationUserInterfaceIdiomKeyUIStateRestorationViewControllerStoryboardKey key来命名。UIKit使用这些key来存储视图控制器状态的额外信息。

关于视图控制器编码解码方法的更多信息,参见UIViewcontroller类引用(UIViewController Class Reference)。

保存和恢复视图控制器技巧

在视图控制器中添加状态保存与恢复,考虑以下指南:

· 请记住可能不希望保存所有视图控制器。在某些情况下,保存某个视图控制器没有意义。例如,如果应用显示一个变更,你希望取消操作并恢复到应用的前一个界面。在这种情况下,你不用保存该视图控制器。

· 恢复过程中避免交换视图控制器类。状态保存系统为其保存的视图控制器类编码。在恢复期间,如果你的应用返回一个不匹配原始对象(或不是原始对象的子类)的对象,系统不要求视图控制器解码任何状态信息。因此,划掉旧的视图控制器换成完全不同的视图控制器,这个过程不恢复该对象的全部状态。

· 状态保存系统希望你有意的使用视图控制器。恢复过程依赖视图控制器的控制关系来重建你的界面。如果你没有正确使用容器视图控制器,保存系统不能找到你的视图控制器。例如,除非相应的视图控制器之间包含某种关系,否则不要在不同的视图中嵌入一个视图控制器的视图。

官方原文地址:

https://developer.apple.com/library/prerelease/ios/featuredarticles/ViewControllerPGforiPhoneOS/PreservingandRestoringState.html#//apple_ref/doc/uid/TP40007457-CH28-SW1

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

推荐阅读更多精彩内容