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

级别: ★★☆☆☆
标签:「iOS」「UIStateRestoration」
作者: 沐灵洛
审校: QiShare团队


前言:上篇我们介绍了UI状态保存和恢复的流程,UIStateRestoration协议类的方法,适用场景,调试策略以及UIApplication、UIViewController、UIView关于UIStateRestoration协议所提供的接口方法。

本篇文章将介绍我们如何实现UI状态保存和恢复。

在AppDelegate.m中设置UI的状态可以恢复和保存

- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
    return YES;
}
- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder {
    return YES;
}

相应的UIViewController中重写以下方法
//进入后台时调用;使用此方法保存我们需要下次恢复的数据。
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder; {
    [super encodeRestorableStateWithCoder:coder];
   //保存数据的代码写在这里
  [coder encodeObject: _nameTextField.text ?: @"" forKey:nameKey];
    
}
//进入前台时调用;使用此方法恢复数据,并展示。
- (void)decodeRestorableStateWithCoder:(NSCoder *)coder; {
    [super decodeRestorableStateWithCoder:coder];
    self.name =  [NSString stringWithString:[coder decodeObjectForKey:nameKey]];
    _nameTextField.text = self.name;
}

设置完这两项,真的就可以了吗?我们可能会发现新建一个工程,直接使用自带的ViewController打个断点,发现成功调用UIViewController中重写的encodeRestorableStateWithCoderdecodeRestorableStateWithCoder方法,进行了数据的保存。但是使用UINavigationController 或者UITabBarController进行多层嵌套后,以上方法却没有被调用。其实这一切只是因为Xcode给我配置的初始项目中,ViewController是主window的根控制器,不存在UITabBarController或UINavigationController的嵌套,界面展示的控制器显示单一,也不会存在多层,并且此ViewController还是直接从故事版实例化的。

场景2:
主window的根控制器为以ViewController A 初始化的一个UINavigationController,在ViewController A中有一个按钮点击跳转进入ViewController B,此时使用调试方法,让程序退出。再次启动UI状态是否恢复到ViewController B。

按照场景2,我们需要恢复到ViewController B,若不管中间的控制器ViewController A,NavigationController便会断层,显示这不是我们想要的;所以我们需要在应用重启时,不仅还原ViewController B,还希望ViewController A按照层级还原,如若ViewController A中还有要恢复的数据,也一并恢复。

嵌套控制器设置

逐层设置restorationIdentifier,并重写相应的保存与恢复方法

  1. storyboard实例化的控制器设置恢复标识
storyboard设置restorationIdentifier
  1. 代码设置恢复标识
self.restorationIdentifier = NSStringFromClass(self.class);

注意:

所有通向ViewController B的视图控制器必须具有还原标识符(包括初始的UINavigationController,UITabBarController),否则状态还原将无法工作。即:需要设置restorationIdentifier

嵌套控制器的恢复

方案一

  1. 设置ViewController中定义的restorationClass属性。
 //! 设置恢复标识
self.restorationIdentifier = NSStringFromClass(self.class);
//! 设置用于恢复的类
self.restorationClass = self.class;

restorationClass:Class的实例对象,APP状态恢复的时候负责重新创建当前的控制器 ,需要实现定义在UIStateRestoring.h中的UIViewControllerRestoration协议。restorationClass可以是当前控制器也可以是其他对象,只要实现了UIViewControllerRestoration协议即可。

  1. 在指定的restorationClass中恢复当前控制器。
+ (nullable UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
    //! identifierComponents返回的就是我们之前设置的restorationIdentifier
    PersonDetailController *ctrl = [[PersonDetailController alloc]init];
    ctrl.restorationIdentifier = identifierComponents.lastObject;
    ctrl.restorationClass = [self class];
    return ctrl;
}

总结:多层控制器,每层控制器都需要在所属的类中设置restorationClass同时必须实现UIViewControllerRestoration方法,两者缺一不可。

方案二
多层级嵌套时,每个控制器中不需要单独设置restorationClass,或者每个控制都没有指定restorationClass时。则需要实现UIApplication对于UIStateRestoration协议所实现接口方法,让我们可以在恢复期间创建每个层级的控制器。

- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
   
    UIViewController *vc;
    UIStoryboard *storyboard = [coder decodeObjectForKey:UIStateRestorationViewControllerStoryboardKey];
    if (storyboard){
        return nil;
    } else {
      vc = [[NSClassFromString(identifierComponents.lastObject) alloc]init];
    }

    return vc;
}

上述代码中,为什么从storyboard恢复的部分,就直接返回nil了呢?为什么不使用如下方式把控制器实例化完成呢?:

vc = [storyboard instantiateViewControllerWithIdentifier:identifierComponents.lastObject];
vc.restorationIdentifier = [identifierComponents lastObject];
vc.restorationClass = NSClassFromString(identifierComponents.lastObject);

在笔者的亲测过程中发现这样做会多实例化一次vc对象,会影响vc界面恢复的数据展示。这是因为来自storyboard的视图,会由UIKIT 自动帮我们查找和创建视图控制器。
总结:多层控制器统一在AppDelegate中实现各个层级控制器的恢复,比较方便。

注意:

1.通向ViewController B的视图控制器若实现restorationClass和UIViewControllerRestoration组合后,则不会调用UIApplication对于UIStateRestoration协议所实现接口方法,否则恢复时回调用。
2.如果我们没有指明,恢复每一个控制器时 用于创建此控制器的对象所属的类,则必须在AppDelegate中实现此方法,让我们可以在恢复期间创建一个新的控制器。
3.来自故事版的视图,恢复时会由UIKIT 自动帮我们查找和创建视图控制器。

至此我们的应用应该具备简单UI的状态恢复和保存功能。下篇文章我们将介绍UIStateRestoration协议类中的UIDataSourceModelAssociation协议。
QIRestorationDemo地址


推荐文章:
iOS UI状态保存和恢复(一)
Swift 运算符
iOS 中精确定时的常用方法
Sign In With Apple(一)
算法小专栏:动态规划(一)
Dart基础(一)
Dart基础(二)
Dart基础(三)
Dart基础(四)
iOS 短信验证码倒计时按钮

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

推荐阅读更多精彩内容