iOS UI狀態保存和恢復(一)

級別: ★★☆☆☆
標簽:「iOS」「UIStateRestoration」
作者: 沐靈洛
審校: QiShare團隊


前言:iOS 開發中,我們都知道一個App點擊了home按鍵或者切換至其他應用時,將進入后臺。隨著時間的推移,App會經歷后臺運行,后臺懸掛,最后被殺死。假如有這樣一個場景:

場景1:用戶正在使用我們App進行個人信息的編輯,突然接到了一個電話,使得App進入后臺并且通話時間超過了App后臺保活的時間。當用戶通話完畢的時候,返回繼續填寫,卻發現App重新啟動了,并且用戶之前填寫的數據,都沒有保存,需要重新輸入?用戶的體驗會很不好。

對于此問題,我們可能會說讓App后臺保持活躍不就行啦。是的,這是個很好的解決方案。但是除了這個方案,我們是不是有其他的辦法實現UI界面和數據的保存和恢復。答案是肯定的,接下來我們會介紹一種方案UIStateRestoration

一、關于UIStateRestoration

UIStateRestoration出現于iOS 6.0以后的API中。主要幫助我們實現特定場景下的UI保存和恢復。UIStateRestoration是一個協議類,在蘋果的系統中UIKit框架下的UIApplication、UIViewController、UIView都實現了UIStateRestoration協議。

關于UI狀態從應用程序啟動到恢復以及UI狀態保存時相關API的調用順序,用官網的圖解大家可以理解的更清楚。

UI狀態從應用程序啟動到恢復調用順序說明
UI狀態保存時調用順序說明

UI狀態恢復,只有當AppDelegate實現application:shouldRestoreApplicationState:并且在方法中返回true時才會生效。
UI狀態保存,只有當AppDelegate實現application: shouldSaveApplicationState:并且在方法中返回true時才會生效。

二、UIStateRestoration的介紹

  1. 系統進行UI狀態的保存和恢復時,自動使用以下常量字符串,進行相關數據的歸檔。
#pragma mark -- State Restoration Coder Keys --
// UIStoryBoard that originally created the ViewController that saved state, nil if no UIStoryboard
//保存和創建一個故事版用到的key
UIKIT_EXTERN NSString *const UIStateRestorationViewControllerStoryboardKey NS_AVAILABLE_IOS(6_0);
// NSString with value of info.plist's Bundle Version (app version) when state was last saved for the app
//應用程序上次狀態保存時info.plist的應用程序版本
UIKIT_EXTERN NSString *const UIApplicationStateRestorationBundleVersionKey NS_AVAILABLE_IOS(6_0);
// NSNumber containing the UIUserInterfaceIdiom enum value of the app that saved state
//狀態保存時應用程序的`UIUserInterfaceIdiom`枚舉值
UIKIT_EXTERN NSString *const UIApplicationStateRestorationUserInterfaceIdiomKey NS_AVAILABLE_IOS(6_0);
// NSDate specifying the date/time the state restoration archive was saved. This is in UTC.
//狀態保存的時間,UTC格式。
UIKIT_EXTERN NSString *const UIApplicationStateRestorationTimestampKey NS_AVAILABLE_IOS(7_0);
// NSString with value of the system version (iOS version) when state was last saved for the app
//上次應用程序保存狀態時的系統版本(iOS版本)
UIKIT_EXTERN NSString *const UIApplicationStateRestorationSystemVersionKey NS_AVAILABLE_IOS(7_0);
  1. UIViewControllerRestoration協議:在UI狀態恢復時幫我們生成一個控制器。
#pragma mark -- State Restoration protocols for UIView and UIViewController --
// A class must implement this protocol if it is specified as the restoration class of a UIViewController.
//如果將類指定為UIViewController的恢復類,則必須實現此協議。
@protocol UIViewControllerRestoration
+ (nullable UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder;
@end
  1. UIDataSourceModelAssociation協議:目前只有UITableView and UICollectionView實現了這個協議。
    官網說明: UIDataSourceModelAssociation.
@protocol UIDataSourceModelAssociation
- (nullable NSString *) modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view;
- (nullable NSIndexPath *) indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view;
@end
  1. UIStateRestoring協議:實現UIStateRestoring協議,可以讓我們自定義的視圖(非UIView和UIViewController子類)加入狀態恢復。前提必須使用UIApplication的+ (void)registerObjectForStateRestoration:(id<UIStateRestoring>)object restorationIdentifier:(NSString *)restorationIdentifier方法注冊。
+ (void)registerObjectForStateRestoration:(id<UIStateRestoring>)object restorationIdentifier:(NSString *)restorationIdentifier
@protocol UIObjectRestoration;
// Conform to this protocol if you want your objects to participate in state restoration. 
// To participate in state restoration, the function registerObjectForStateRestoration must
// be called for the object.
/*如果您希望對象參與狀態恢復,請遵守此協議。
要參與狀態恢復,函數registerObjectForStateRestoration必須為此對象而調用。*/
@protocol UIStateRestoring <NSObject>
@optional
// The parent property is used to scope the restoration identifier path for an object, to
// disambiguate it from other objects that might be using the same identifier. The parent
// must be a restorable object or a view controller, else it will be ignored.
/*parent屬性用于定義一個對象的恢復標識恢復路徑,以便從可能使用相同恢復標識的其他對象中消除歧義。
parent屬性必須是可恢復對象`id<UIStateRestoring> `或視圖控制器,否則將被忽略。
個人理解:類似繼承體系模式,方便歸整清楚恢復的路徑,幫助我們進行一定順序和層次的恢復。*/
@property (nonatomic, readonly, nullable) id<UIStateRestoring> restorationParent;
// The restoration class specifies a class which is consulted during restoration to find/create
// the object, rather than trying to look it up implicitly
/*
objectRestorationClass指定在恢復期間用于查找和創建需要恢復的對象的類。
并不是試圖隱式查找和創建需要恢復的對象
*/
@property (nonatomic, readonly, nullable) Class<UIObjectRestoration> objectRestorationClass;
// Methods to save and restore state for the object. If these aren't implemented, the object
// can still be referenced by other objects in state restoration archives, but it won't
// save/restore any state of its own.
/*
保存和恢復對象狀態的方法。
如果沒有實現這些方法,對象仍可以被狀態恢復歸檔中的其他對象引用,但它將不會保存和恢復自己的任何狀態。
*/
- (void) encodeRestorableStateWithCoder:(NSCoder *)coder;
- (void) decodeRestorableStateWithCoder:(NSCoder *)coder;
// applicationFinishedRestoringState is called on all restored objects that implement the method *after* all other object
// decoding has been done (including the application delegate). This allows an object to complete setup after state
// restoration, knowing that all objects from the restoration archive have decoded their state.
/*在所有其他對象實現恢復方法,解碼完成(包括`AppDelegate`的解碼)并恢復了所有的可恢復對象后才會調用applicationFinishedRestoringState。
這允許對象在狀態恢復之后完成設置,可以通過此方法明確知道恢復檔案中的所有對象都已解碼其狀態
*/
- (void) applicationFinishedRestoringState;
@end
// Protocol for classes that act as a factory to find a restorable object during state restoration
// A class must implement this protocol if it is specified as the restoration class of a UIRestorableObject.
//作為工廠類的協議,用于在狀態恢復期間查找可恢復對象。如果指定某個類為`id<UIStateRestoring>`的`objectRestorationClass `,則該類必須實現此協議。
@protocol UIObjectRestoration
+ (nullable id<UIStateRestoring>) objectWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder;
@end

UIStateRestoration場景

適用于App進入后臺,后臺停留時間超過系統分配的后臺活躍時間后被系統殺死時的場景。因為當用戶強制退出應用程序時,系統會自動刪除應用程序的保留狀態。在應用程序被終止時刪除保留的狀態信息是一項安全預防措施。如果應用程序在啟動時崩潰,系統也會刪除保留狀態作為類似的安全預防措施。

UIStateRestoration調試

根據場景描述,如果要測試應用程序恢復其狀態的能力,則在調試期間不應使用多任務欄來強制終止應用程序。可以通過設置項目的plist文件下Application does not run in background為YES。

UIApplication對于UIStateRestoration協議的實現接口
#pragma mark -- State Restoration protocol adopted by UIApplication delegate --
- (nullable UIViewController *) application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (BOOL) application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (BOOL) application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (void) application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (void) application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
UIViewController對于UIStateRestoration協議的實現接口
@interface UIViewController (UIStateRestoration) <UIStateRestoring>
@property (nullable, nonatomic, copy) NSString *restorationIdentifier NS_AVAILABLE_IOS(6_0);
@property (nullable, nonatomic, readwrite, assign) Class<UIViewControllerRestoration> restorationClass NS_AVAILABLE_IOS(6_0);
- (void) encodeRestorableStateWithCoder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (void) decodeRestorableStateWithCoder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (void) applicationFinishedRestoringState NS_AVAILABLE_IOS(7_0);
@end
UIView對于UIStateRestoration協議的實現接口
@interface UIView (UIStateRestoration)
@property (nullable, nonatomic, copy) NSString *restorationIdentifier NS_AVAILABLE_IOS(6_0);
- (void) encodeRestorableStateWithCoder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
- (void) decodeRestorableStateWithCoder:(NSCoder *)coder NS_AVAILABLE_IOS(6_0);
@end

本篇我們介紹了UI狀態保存和恢復的流程,UIStateRestoration協議類的方法,適用場景,調試策略以及UIApplication、UIViewController、UIView關于1UIStateRestoration1協議所提供的接口方法。
下篇文章我們將介紹如何實現UI狀態保存和恢復。


推薦文章:
Swift 運算符
iOS 中精確定時的常用方法
Sign In With Apple(一)
算法小專欄:動態規劃(一)
Dart基礎(一)
Dart基礎(二)
Dart基礎(三)
Dart基礎(四)
iOS 短信驗證碼倒計時按鈕

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容