前言
概述
ViewController在App狀態(tài)的保存和恢復(fù)過程中扮演者重要的角色涨缚。狀態(tài)保存會在App掛起之前記錄App的配置虾标,在其重新啟動的時候可以恢復(fù)其配置信息涯雅。將App恢復(fù)到之前的配置可以為用戶節(jié)省時間并提供更好的用戶體驗景埃。
狀態(tài)的保存和恢復(fù)過程幾乎都是自動執(zhí)行的,但是你需要告知iOS系統(tǒng),你的App的那一部分將被保存,下面的步驟展示了如何保存你的App的ViewController狀態(tài):
- (必選)將恢復(fù)標(biāo)識賦值給你想保存狀態(tài)的ViewController赫粥。參考下節(jié)。
- (必須)告知iOS系統(tǒng)如何在啟動的時候創(chuàng)建或者定位一個新的ViewController予借。
- (可選)對于不同的ViewController越平,存儲原始圖控制器的指定的配置數(shù)據(jù),以在新的視圖控制器中恢復(fù)其配置灵迫。
有關(guān)App狀態(tài)的保存和恢復(fù)流程的概述秦叛,參看:App Programming Guide for iOS。
標(biāo)記ViewController進(jìn)行保存
UIKit只保留你告訴它保存的視圖控制器瀑粥。每個ViewController都有一個restorationIdentifier
屬性挣跋,默認(rèn)情況下該屬性是nil。為該屬性指定一個可用字符串來告知UIKit當(dāng)前ViewController和它的視圖應(yīng)該被保存狞换。你也可以在Xib中為該屬性賦值避咆。
當(dāng)為某一個ViewController賦值restorationIdentifier
屬性的時候,要記得為該ViewController視圖結(jié)構(gòu)中的上層ViewController也賦值該屬性的值修噪。在保存過程中查库,UIKit從window的根視圖控制器開始并走向視圖控制器的層次結(jié)構(gòu)。如果某一個ViewController的沒有為該屬性賦值黄琼,那么它以及它的子視圖控制器都將被忽略樊销。
選擇有效的恢復(fù)標(biāo)識
UIKit使用您的恢復(fù)標(biāo)識字符串來重新創(chuàng)建視圖控制器,因此請選擇易于識別您的代碼的字符串脏款。如果UIKit無法自動創(chuàng)建一個視圖控制器现柠,它會要求你創(chuàng)建視圖控制器,并為你提供視圖控制器及其所有父視圖控制器的恢復(fù)標(biāo)識符弛矛。這個標(biāo)識符鏈接標(biāo)識視圖控制器的恢復(fù)路徑够吩,以及您如何確定請求那個視圖控制器≌擅ィ恢復(fù)路徑從根視圖控制器開始周循,并包含每個視圖控制器直到并包括請求的視圖控制器。
恢復(fù)標(biāo)識經(jīng)常使用ViewController的類名來表示万俗。如果你在多個地方使用了改類湾笛,你需要使用更有意義的標(biāo)識。比如:你可以根據(jù)視圖控制器管理的數(shù)據(jù)分配一個字符串闰歪。
每個ViewController的恢復(fù)路徑必須是唯一的嚎研。如果一個容器型視圖控制器有兩個子視圖,容器視圖必須為每個子視圖控制器分配一個唯一的標(biāo)識。UIKit中的某些容器型視圖控制器會自動消除子視圖控制器間的歧義临扮,允許你使用相同的標(biāo)識符標(biāo)識每個子視圖控制器论矾。比如說UINavgationController
為每個子視圖控制器的標(biāo)識信息添加其在導(dǎo)航控制器的堆棧位置信息。關(guān)于給定視圖控制器的更多信息杆勇,參考響應(yīng)的類指南贪壳。
關(guān)于如何使用恢復(fù)標(biāo)識和恢復(fù)路徑創(chuàng)建ViewController的更多信息,參考:在啟動的時候恢復(fù)ViewController蚜退。
移除視圖控制器組
為了在恢復(fù)流程中移除整個視圖控制器組闰靴,設(shè)置父視圖控制器的恢復(fù)標(biāo)識為nil。下圖展示了在ViewController的視圖層級中將恢復(fù)標(biāo)識設(shè)置為nil造成的影響钻注。缺乏保存數(shù)據(jù)會阻止稍后的恢復(fù)視圖控制器蚂且。
排除一個或多個視圖控制器并不會在恢復(fù)過程中刪除它們。在啟動時幅恋,app的任何一個視圖控制器都會按照默認(rèn)設(shè)置被創(chuàng)建杏死。如下圖所示,視圖控制器仍會在默認(rèn)的配置下重建佳遣。
從自動保存過程中排除視圖控制器不會阻止您手動保存視圖控制器识埋。
在恢復(fù)歸檔中保存對視圖控制器的引用可保留視圖控制器及其中狀態(tài)信息凡伊。比如說:第一張圖中零渐,導(dǎo)航控制器中有三個子視圖控制器,它們的狀態(tài)會被保存系忙,在恢復(fù)期間诵盼,應(yīng)用程序委托可以創(chuàng)建這些視圖控制器并將它們推送到導(dǎo)航控制器的堆棧中。
保存視圖控制器的視圖狀態(tài)
一些視圖有一些額外與自身相關(guān)的額外的狀態(tài)信息银还,這些信息與它所在的ViewController無關(guān)风宁,比如說,你想保存scrollView滾動的位置蛹疯。但是ViewController只是為scroll view 提供其展示的內(nèi)容戒财,scroll view 自身擔(dān)負(fù)著存儲其顯示狀態(tài)。
為了保存視圖的狀態(tài)捺弦,按照如下操作:
- 為View的
restorationIdentifier
屬性賦值饮寞。 - 為View所在的ViewController的
restorationIdentifier
賦值 - 對于tableView和colletionView, 實現(xiàn)
UIDataSourceModelAssociation
協(xié)議,并提供數(shù)據(jù)源列吼。
為View賦值restorationIdentifier告知UIKit應(yīng)當(dāng)在存儲的時候講View的狀態(tài)寫入歸檔文件中幽崩,當(dāng)之后ViewController恢復(fù)的時候,UIKit同時也恢復(fù)那些擁有restorationIdentifier的視圖寞钥。
在啟動的時候恢復(fù)視圖控制器
在啟動時慌申,UIKit嘗試恢復(fù)到它先前的狀態(tài)。這時理郑,UIKit要求你的app創(chuàng)建(或者定位)ViewController對象蹄溉,這些對象中包含了你存儲的用戶界面咨油。嘗試定位視圖控制器的時候,UIKit安裝如下的順序進(jìn)行搜索类缤。
- 如果視圖控制器擁有一個恢復(fù)類臼勉,UIKit要求該恢復(fù)類提供視圖控制器。UIKit調(diào)用相關(guān)恢復(fù)類的
viewControllerWithRestorationIdentifierPath:coder:
方法來返回視圖控制器餐弱,如果該方法返回nil宴霸,意味著應(yīng)用程序不想重建這個視圖控制器,并且UIKit停止搜索這個控制器膏蚓。 - 如果視圖控制器沒有恢復(fù)類瓢谢,UIKit要求appdelegate提供這個視圖控制器。在沒有恢復(fù)類的時候驮瞧,UIKit調(diào)用appdelegate的
application:viewControllerWithRestorationIdentifierPath:coder:
方法來尋找視圖控制器氓扛。如果這個方法返回nil,UIKit視圖隱式的查找視圖控制器论笔。 - 如果視圖控制器擁有正確的還原路徑采郎,則UIKit將使用該對象。如果你的app在啟動的時候創(chuàng)建ViewController(不管是通過編碼的形式還是通過Storyboard)狂魔,并且這些ViewController有用恢復(fù)標(biāo)識蒜埋,UIKit隱式的基于恢復(fù)路徑發(fā)現(xiàn)它們。
- 如果ViewController之前是通過Storyboard文件加載的最楷,UIKit使用被保存的Storyboard信心來定位并創(chuàng)建它整份。UIKit將有關(guān)視圖控制器的Storyboard信息保存在恢復(fù)歸檔中。在恢復(fù)時籽孙,如果視圖控制器沒用被通過其他的方式找到烈评,UIKit使用這些信息來定位同一個Storyboard文件并且實例化響應(yīng)的ViewController。
將恢復(fù)類賦值給視圖控制器可以避免UIKit通過隱式的方式查找視圖控制器犯建。使用恢復(fù)類可以更好的控制你是否真的想創(chuàng)建視圖控制器讲冠。比如說,如果你的類確定不應(yīng)該創(chuàng)建視圖控制器适瓦,你的viewControllerWithRestorationIdentifierPath:coder:
方法可以返回nil竿开。當(dāng)沒有恢復(fù)類時,UIKit會盡其所能找到或創(chuàng)建視圖控制器并將其恢復(fù)犹菇。
當(dāng)使用恢復(fù)類的時候德迹,你的viewControllerWithRestorationIdentifierPath:coder:
方法應(yīng)當(dāng)創(chuàng)建一個新的實例,執(zhí)行最小的初始化并返回結(jié)果揭芍。下面代碼展示了一個例子胳搞,如何使用該方法從Storyboard中加載視圖控制器,因為視圖控制器通常從Storyboard中被加載,這個方使用UIStateRestorationViewControllerStoryboardKey
鍵來從歸檔文件中獲取Storyboard肌毅。請注意筷转,此方法不會嘗試配置視圖控制器的數(shù)據(jù)字段。當(dāng)視圖控制器的狀態(tài)被解碼時悬而,稍后發(fā)生該步驟呜舒。
+ (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;
}
重新分配恢復(fù)標(biāo)識符和恢復(fù)類是手動重新創(chuàng)建視圖控制器時采用的良好習(xí)慣”康欤恢復(fù)恢復(fù)標(biāo)識符的最簡單方法是獲取identifierComponents
數(shù)組中的最后一項并將其分配給您的視圖控制器袭蝗。
對于在啟動時從應(yīng)用程序主要故事板文件創(chuàng)建的對象,請勿為每個對象創(chuàng)建新實例般婆。讓UIKit隱式查找這些對象或使用應(yīng)用程序的viewControllerWithRestorationIdentifierPath:coder:
您的應(yīng)用程序委托的方法來查找現(xiàn)有對象到腥。
對視圖控制器的狀態(tài)進(jìn)行編碼和解碼
對于每一個要保存的對象,UIKit調(diào)用對象的encodRestorableStateWithCoder:
方法來保存它的狀態(tài)蔚袍。在恢復(fù)過程中乡范,UIKit調(diào)用對應(yīng)的decodeRestorableStateWithCoder:
方法來解碼狀態(tài)并應(yīng)用到該對象上。這兩個方法的實現(xiàn)都是可選的啤咽,但推薦你實現(xiàn)它們晋辆。你可以使用它們來存儲和恢復(fù)下面信息:
- 對所有顯示數(shù)據(jù)的引用
- 對于容器型視圖控制器,引用其子視圖控制器宇整。
- 當(dāng)前選擇的信息瓶佳。
- 對于用戶可配置的視圖控制器,當(dāng)前視圖的配置信息没陡。
在endcode 和 decode方法中涩哟,你可以編碼任何支持coder的數(shù)據(jù)類型和對象索赏。對于任何希望被編碼的視圖或者視圖控制器盼玄,對象必須實現(xiàn)NSCoding
協(xié)議并使用該協(xié)議的方法來寫入它的狀態(tài)。對于視圖和視圖控制器潜腻,編碼器不使用NSCoding
協(xié)議來存儲對象的狀態(tài)埃儿,相反,編碼器存儲對象的恢復(fù)標(biāo)識并將它添加到存儲對象列表融涣,這會導(dǎo)致該對象的encoderRestorableStateWithCoder:
方法被調(diào)用童番。
ViewController的encodeRestorableStateWithCoder :
和decodeRestorableStateWithCoder :
方法必須在其實現(xiàn)中調(diào)用其super方法,調(diào)用super方法讓父類可以保存和恢復(fù)額外的信息威鹿。下面代碼實現(xiàn)了一個簡單的例子剃斧,使用這些方法來保存ViewController的一個數(shù)值信息。
- (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];
}
編碼器對象在編碼和解碼的過程中不共享忽你。每個可保存狀態(tài)的對象保留這它自己的編碼器對象幼东。使用獨立的編碼器意味著你不必?fù)?dān)心鍵之間的命名沖突。但是不要使用UIApplicationStateRestorationBundleVersionKey
, UIApplicationStateRestorationUserInterfaceIdiomKe
, 和 UIStateRestorationViewControllerStoryboardKey
作為你的鍵。這些鍵被UIKit對象用來存儲一些你的ViewController的額外信息根蟹。
關(guān)于更多實現(xiàn)編碼和解碼的方法的信息脓杉,參考UIViewController Class Reference
保存和恢復(fù)ViewController的一些建議
在你給ViewController添加對狀態(tài)的保存和恢復(fù)時,考慮以下幾個原則:
- 記住你可能不需要存儲所有的VIewController简逮。在某些情況下球散,保存ViewController是沒有意義的,比如說散庶,如果你的APP正在顯示一個變化操作蕉堰,則可能需要取消操作并將應(yīng)用程序的狀態(tài)恢復(fù)到上一個屏幕。在這種情況下悲龟,你不會保存要求輸入新密碼信息的視圖控制器嘁灯。
- 避免在恢復(fù)過程中交換視圖控制器類。狀態(tài)保存系統(tǒng)編碼它保存的視圖控制器的類躲舌。在恢復(fù)過程中丑婿,如果你的應(yīng)用程序返回了一個與原先的類不相符的類,系統(tǒng)不會要求視圖控制器解碼任何狀態(tài)信息没卸。因此羹奉,換掉一個舊的視圖控制器不會恢復(fù)對象的完整性。
- 狀態(tài)保存系統(tǒng)希望你按照它們的預(yù)期使用ViewController约计【魇茫恢復(fù)過程依賴視圖控制器的包含關(guān)系來重建你的界面。如果您沒有正確使用容器視圖控制器煤蚌,保存系統(tǒng)將找不到您的視圖控制器耕挨。例如,除非在相應(yīng)的視圖控制器之間存在包含關(guān)系尉桩,否則絕不要在不同的視圖中嵌入視圖控制器的視圖筒占。