iOS UI状态保存和恢复(一)

avatar
奇舞团移动端团队 @奇舞团

级别: ★★☆☆☆
标签:「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 短信验证码倒计时按钮
奇舞周刊