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阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容