級別: ★★☆☆☆
標簽:「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狀態恢復,只有當AppDelegate實現application:shouldRestoreApplicationState:
并且在方法中返回true時才會生效。
UI狀態保存,只有當AppDelegate實現application: shouldSaveApplicationState:
并且在方法中返回true時才會生效。
二、UIStateRestoration的介紹
- 系統進行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);
- 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
- UIDataSourceModelAssociation協議:目前只有UITableView and UICollectionView實現了這個協議。
官網說明: UIDataSourceModelAssociation.
@protocol UIDataSourceModelAssociation
- (nullable NSString *) modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view;
- (nullable NSIndexPath *) indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view;
@end
- 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 短信驗證碼倒計時按鈕