視圖控制器在保存和恢復(fù)過(guò)程中起著非常重要的作用泡孩。狀態(tài)保存記錄應(yīng)用中斷前的配置于个,在隨后應(yīng)用啟動(dòng)恢復(fù)配置痛悯。將應(yīng)用恢復(fù)到先前的配置可以為用戶節(jié)省時(shí)間,并提供一個(gè)更好的用戶體驗(yàn)会钝。
保存和恢復(fù)過(guò)程是自動(dòng)的,但你需要告訴iOS疑俭,需要保留應(yīng)用的哪些部分遥巴。保存應(yīng)用視圖控制器的步驟如下:
· (必須)分配恢復(fù)標(biāo)示符到你想保存的視圖控制器上,參見標(biāo)記保存視圖控制器( Tagging View Controllers for Preservation)
· (必須)告訴iOS如何在啟動(dòng)時(shí)創(chuàng)建或查找新視圖控制器對(duì)象恶导,參見啟動(dòng)時(shí)恢復(fù)視圖控制器( Restoring View Controllers at Launch Time)浆竭。
· (可選)每個(gè)視圖控制器,存儲(chǔ)任何特定配置數(shù)據(jù)使視圖控制器返回到原來(lái)的配置,參見視圖控制器狀態(tài)的編碼和解碼(Encoding and Decoding Your View Controller’s State)邦泄。
保存和恢復(fù)的過(guò)程的概述删窒,參見iOS應(yīng)用編程指南(App Programming Guide for iOS)。
標(biāo)記保存視圖控制器
UIKit只保存你告訴它要保存的視圖控制器顺囊。每個(gè)視圖控制器有一個(gè)restorationIdentifier屬性肌索,這個(gè)屬性的默認(rèn)值為nil。設(shè)置這個(gè)屬性值為有效字符串特碳,告訴UIKit視圖控制器和視圖應(yīng)該要保存诚亚。可以以編程的方式或者在storyboard文件中設(shè)置恢復(fù)標(biāo)示符午乓。
當(dāng)分配恢復(fù)標(biāo)示符時(shí)站宗,在視圖層級(jí)結(jié)構(gòu)中的所有的父視圖控制器必須有恢復(fù)標(biāo)示符。在保存過(guò)程中益愈,UIKit從窗口的根視圖控制器開始并遍歷視圖控制器層級(jí)結(jié)構(gòu)梢灭。如果該層級(jí)結(jié)構(gòu)中一個(gè)視圖控制器沒有恢復(fù)標(biāo)示符,該視圖控制器和其所有子視圖控制器和present的視圖控制器都會(huì)被忽略蒸其。
選擇有效的恢復(fù)標(biāo)示符
UIKit使用恢復(fù)標(biāo)示符字符串在之后重新創(chuàng)建視圖控制器敏释,所以必須選擇代碼容易識(shí)別的字符串。如果UIKit不能自動(dòng)創(chuàng)建一個(gè)視圖控制器摸袁,要求你創(chuàng)建钥顽,UIKit會(huì)提供視圖控制器和其所有父視圖控制器的恢復(fù)標(biāo)示符。這一串的標(biāo)示符代表視圖控制器的恢復(fù)路徑靠汁,及如何決定哪些視圖控制器被請(qǐng)求蜂大。恢復(fù)路徑從根視圖控制器開始膀曾,包括呈現(xiàn)的每個(gè)視圖控制器和被請(qǐng)求的視圖控制器县爬。
恢復(fù)標(biāo)示圖通常以視圖控制器類名命名。如果你在很多地方使用相同的類添谊,你可能希望分配更有意義的值财喳。例如,你可以基于視圖控制器管理的數(shù)據(jù)來(lái)指定字符串斩狱。
每個(gè)視圖控制器的恢復(fù)路徑必須是唯一的耳高。如果一個(gè)容器視圖控制器有兩個(gè)子視圖,容器必須為每個(gè)子視圖控制器分配一個(gè)獨(dú)特的恢復(fù)標(biāo)示符所踊。一些UIKit中容器視圖控制器會(huì)自動(dòng)消除歧義子視圖控制器泌枪,允許為每個(gè)子視圖控制器使用相同恢復(fù)標(biāo)示符。例如秕岛,類根據(jù)每個(gè)子視圖控制器在導(dǎo)航堆棧中的位置添加信息碌燕。關(guān)于一個(gè)給定視圖控制器的行為更多信息误证,參見相應(yīng)的類引用。
如何使用恢復(fù)標(biāo)示符和恢復(fù)路徑創(chuàng)建視圖控制器的更多信息修壕,參見啟動(dòng)時(shí)恢復(fù)視圖控制器(Restoring View Controllers at Launch Time)愈捅。
不包括視圖控制器組
在恢復(fù)過(guò)程中不包括整個(gè)視圖控制器組,設(shè)置父視圖控制器的恢復(fù)標(biāo)示符為nil慈鸠。圖7-1展示了設(shè)置恢復(fù)標(biāo)示符為nil對(duì)視圖層級(jí)結(jié)構(gòu)的影響蓝谨。缺少保存數(shù)據(jù),在稍后無(wú)法恢復(fù)視圖控制器青团。
在隨后的恢復(fù)過(guò)程中譬巫,并不完全刪除其中不包括的一個(gè)或多個(gè)視圖控制器。在啟動(dòng)時(shí)督笆,任何視圖控制器都是應(yīng)用的一部分芦昔,默認(rèn)創(chuàng)建設(shè)置,如圖7-2所示娃肿。按照默認(rèn)配置創(chuàng)建視圖控制器烟零。
自動(dòng)保存過(guò)程中,可以手動(dòng)保存不包括的視圖控制器咸作。保存視圖控制器的引用即保存視圖控制器和其狀態(tài)信息。例如宵睦,如果圖7-1中的應(yīng)用代理保存導(dǎo)航控制器的三個(gè)子視圖控制器记罚,他們的狀態(tài)將被保存下來(lái)。在恢復(fù)過(guò)程中壳嚎,應(yīng)用代理可以重現(xiàn)創(chuàng)建這些視圖控制器并push他們到導(dǎo)航控制器的堆棧上桐智。
保存視圖控制器的視圖
一些視圖有更多與視圖而不是父視圖控制器相關(guān)的狀態(tài)信息。例如烟馅,你希望保存滾動(dòng)視圖的一個(gè)滾動(dòng)位置说庭。視圖控制器負(fù)責(zé)提供滾動(dòng)視圖的內(nèi)容,而滾動(dòng)視圖本身負(fù)責(zé)保存其視覺狀態(tài)郑趁。
保存視圖狀態(tài)刊驴,執(zhí)行以下操作:
· 指定為 restoration Identifier 屬性一個(gè)有效的字符串。
· 使用具有有效標(biāo)示符的視圖控制器的視圖寡润。
· 對(duì)于表視圖和集合視圖捆憎,指定采用UI Data Source Model Association 協(xié)議的數(shù)據(jù)源
將標(biāo)示符分配給視圖告訴UIKit應(yīng)該保存視圖狀態(tài)。當(dāng)稍后恢復(fù)視圖控制器梭纹,UIKit也恢復(fù)有恢復(fù)標(biāo)示符的視圖的狀態(tài)躲惰。
在啟動(dòng)時(shí)恢復(fù)視圖控制器
在啟動(dòng)時(shí),UIKit試圖恢復(fù)你的應(yīng)用到之前的狀態(tài)变抽。那時(shí)础拨,UIKit訪問你的應(yīng)用創(chuàng)建(或確定)用戶界面保存的視圖控制器對(duì)象氮块。當(dāng)定位視圖控制器時(shí),UIKit按照以下順序搜索:
**如果視圖控制器有一個(gè)恢復(fù)類诡宗,UIKit訪問該類提供視圖控制器滔蝉。UIKit調(diào)用恢復(fù)類的 [viewControllerWithRestorationIdentifierPath:coder:](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIViewControllerRestoration_protocol/index.html#//apple_ref/occ/intfcm/UIViewControllerRestoration/viewControllerWithRestorationIdentifierPath:coder:) 方法來(lái)檢索視圖控制器。**如果該方法返回nil僚焦,則假設(shè)應(yīng)用并不希望重新創(chuàng)建視圖控制器锰提,UIKit停止尋找該視圖控制器。
**如果視圖控制器沒有恢復(fù)類芳悲,UIKit要求應(yīng)用代理提供視圖控制器立肘。**UIKit調(diào)用應(yīng)用代理的[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:)方法尋找沒有恢復(fù)類的視圖控制器。如果該方法返回nil名扛,UIKit嘗試找到隱藏的視圖控制器谅年。
**如果有正確恢復(fù)路徑的視圖控制器已經(jīng)存在,UIKit使用該對(duì)象肮韧。**如果你的應(yīng)用在啟動(dòng)時(shí)(無(wú)論是以編程的方式還是通過(guò)storyboard加載的方式)創(chuàng)建視圖控制器融蹂,并且這些視圖控制器有恢復(fù)標(biāo)識(shí)符,UIKit基于他們的恢復(fù)路徑隱式的查找他們弄企。
**如果視圖控制器最初是從storyboard文件加載超燃,UIKit使用保存的storyboard信息來(lái)定位并創(chuàng)建它。**UIKit保存視圖控制器的storyboard信息到恢復(fù)文件中拘领。在恢復(fù)時(shí)意乓,UIKit使用這些信息來(lái)定位相同的storyboard文件,如果通過(guò)其他任何方式?jīng)]有找到視圖控制器约素,則實(shí)例化相應(yīng)視圖控制器届良。
將恢復(fù)類分配給視圖控制器可以防止UIKit隱式的搜索該視圖控制器。使用恢復(fù)類讓你可以控制是否真正創(chuàng)建一個(gè)視圖控制器圣猎。例如士葫,如果你的類決定不再重新創(chuàng)建視圖控制器,viewControllerWithRestorationIdentifierPath:coder:
方法可以返回nil送悔。當(dāng)不存在恢復(fù)類慢显,UIKit會(huì)盡可能找到或者創(chuàng)建該視圖控制器來(lái)恢復(fù)它。
當(dāng)使用一個(gè)恢復(fù)類欠啤,viewControllerWithRestorationIdentifierPath:coder:
方法應(yīng)該創(chuàng)建該類的新實(shí)例鳍怨,執(zhí)行初始化,并返回結(jié)果對(duì)象跪妥。列表7-1 展示了如何使用此方法從storyboard中加載視圖控制器的例子鞋喇。因?yàn)樵撘晥D控制器最初從storyboard加載,該方法使用UIStateRestorationViewControllerStoryboardKey key從文件中找到storyboard眉撵。注意:該方法不配置視圖控制器的數(shù)據(jù)字段侦香。當(dāng)解碼視圖控制器的狀態(tài)時(shí)落塑,發(fā)生這一步。
列表7-1 在恢復(fù)過(guò)程中創(chuàng)建一個(gè)新視圖控制器
<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>
當(dāng)手動(dòng)重新創(chuàng)建視圖控制器罐韩,重新分配恢復(fù)標(biāo)識(shí)符和恢復(fù)類是個(gè)好習(xí)慣憾赁。恢復(fù)恢復(fù)標(biāo)識(shí)符的最簡(jiǎn)單的方法是獲取identifierComponents
數(shù)組中的最后一項(xiàng)散吵,并將其分配給你的視圖控制器龙考。
在啟動(dòng)時(shí)由應(yīng)用主要storyboard文件中創(chuàng)建的對(duì)象,不需要為每個(gè)對(duì)象創(chuàng)建新實(shí)例矾睦。讓UIKit隱式的查找這些對(duì)象或者使用應(yīng)用代理的application:viewControllerWithRestorationIdentifierPath:coder:
方法查找這些存在的對(duì)象晦款。
編碼和解碼視圖控制器狀態(tài)
對(duì)于每個(gè)要保留的對(duì)象,UIKit調(diào)用對(duì)象的encodeRestorableStateWithCoder:方法來(lái)保存其狀態(tài)枚冗。在恢復(fù)過(guò)程中缓溅,UIKit調(diào)用對(duì)應(yīng)的decodeRestorableStateWithCoder: 方法來(lái)解碼該狀態(tài)并將其用于該對(duì)象。這些方法的實(shí)現(xiàn)是可選的赁温,但是建議坛怪,實(shí)現(xiàn)。你可能使用它們來(lái)保存和恢復(fù)以下類型的信息:
· 顯示數(shù)據(jù)的引用(不是數(shù)據(jù)本身)
· 對(duì)于容器視圖控制器股囊,其子視圖控制器的引用
· 當(dāng)前選擇的信息
· 對(duì)于用戶可配置視圖的視圖控制器袜匿,當(dāng)前配置視圖的信息
在你的編碼和解碼方法中,你可以編碼對(duì)象和編碼器支持的任何數(shù)據(jù)類型稚疹。除了視圖和視圖控制器的其他所有對(duì)象必須采用 NSCoding協(xié)議并使用協(xié)議中的方法來(lái)保存其狀態(tài)沉帮。對(duì)于視圖和視圖控制器,編碼器不使用 NSCoding協(xié)議來(lái)保存對(duì)象狀態(tài)贫堰。相反,編碼器保存該對(duì)象的恢復(fù)標(biāo)識(shí)符并將其添加到保存對(duì)象列表待牵,這將導(dǎo)致該對(duì)象的encodeRestorableStateWithCoder:
方法被調(diào)用其屏。
視圖控制器的encodeRestorableStateWithCoder:
和decodeRestorableStateWithCoder:
方法必須在實(shí)現(xiàn)的某個(gè)位置調(diào)用super方法。調(diào)用super方法讓父類有機(jī)會(huì)保存和恢復(fù)任何額外的信息缨该。列表7-2展示了這些方法的實(shí)現(xiàn)例子偎行,該方法保存一個(gè)數(shù)值用于識(shí)別指定視圖控制器。
列表7-2 編碼和界面視圖控制器狀態(tài)
<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>
在編碼和解碼過(guò)程中贰拿,編碼對(duì)象不共享蛤袒。每個(gè)保存狀態(tài)的對(duì)象接收自己的編碼對(duì)象。使用獨(dú)特的編碼器表明你不用擔(dān)心key之間的命名沖突膨更。然而妙真,不要使用UIApplicationStateRestorationBundleVersionKey,UIApplicationStateRestorationUserInterfaceIdiomKey和UIStateRestorationViewControllerStoryboardKey key來(lái)命名。UIKit使用這些key來(lái)存儲(chǔ)視圖控制器狀態(tài)的額外信息荚守。
關(guān)于視圖控制器編碼解碼方法的更多信息珍德,參見UIViewcontroller類引用(UIViewController Class Reference)练般。
保存和恢復(fù)視圖控制器技巧
在視圖控制器中添加狀態(tài)保存與恢復(fù),考慮以下指南:
· 請(qǐng)記住可能不希望保存所有視圖控制器锈候。在某些情況下薄料,保存某個(gè)視圖控制器沒有意義。例如泵琳,如果應(yīng)用顯示一個(gè)變更摄职,你希望取消操作并恢復(fù)到應(yīng)用的前一個(gè)界面。在這種情況下获列,你不用保存該視圖控制器谷市。
· 恢復(fù)過(guò)程中避免交換視圖控制器類。狀態(tài)保存系統(tǒng)為其保存的視圖控制器類編碼蛛倦。在恢復(fù)期間歌懒,如果你的應(yīng)用返回一個(gè)不匹配原始對(duì)象(或不是原始對(duì)象的子類)的對(duì)象,系統(tǒng)不要求視圖控制器解碼任何狀態(tài)信息溯壶。因此及皂,劃掉舊的視圖控制器換成完全不同的視圖控制器,這個(gè)過(guò)程不恢復(fù)該對(duì)象的全部狀態(tài)且改。
· 狀態(tài)保存系統(tǒng)希望你有意的使用視圖控制器验烧。恢復(fù)過(guò)程依賴視圖控制器的控制關(guān)系來(lái)重建你的界面又跛。如果你沒有正確使用容器視圖控制器碍拆,保存系統(tǒng)不能找到你的視圖控制器。例如慨蓝,除非相應(yīng)的視圖控制器之間包含某種關(guān)系感混,否則不要在不同的視圖中嵌入一個(gè)視圖控制器的視圖。