原创Blog,转载请注明出处
blog.csdn.net/hello_hwc

前言:
对于一个app来说,没有办法保证每次都是用户正常关闭(双击home,然后关闭)。有可能是用户把app切换到后台,然后由于内存的原因,IOS系统选择关闭掉应用。这时候,就是状态恢复使用的场景。App应当做到的是,用户没有察觉到App在后台被IOS Kill掉了。

本文主要讲诉如何用Storyboard进行状态恢复,下一篇更新如何实现代码State Restoration。

注意:
状态恢复不是持久化存储,换句话说,用户正常关闭App的时候,状态恢复是不会起到任何作用的,因为用户正常关闭App,理所应当这次操作的状态都应该清除。

一 什么是状态相关的信息?

就是那些不容易被重新创建的信息,比如一个tableview,当前选择的是哪行?比如用户的first responser是什么?比如tabController当前选择是哪个页面。

二 如何实现保存状态信息

  1. (必须) app 代理的两个代理方法,要实现。通常直接返回YES即可,当然,也可以判断状态来决定是否保存和恢复。
    application:shouldSaveApplicationState: application:shouldRestoreApplicationState:
  2. (必须)每个想要保存的视图控制器一定要有一个唯一的恢复表示符(restoration identifiers)。如果是用Storyboard上创建的,则在storyboard上设置。如果是代码创建的,使用属性restorationIdentifier 。
  3. (必须)对于storyboard上创建的视图控制器,不需要设置restoration classes。对于那些由代码动态创建的视图控制器,要设置restoration classes来实现保存和恢复。否则,统一在appDelegate的代理方法中实现。后一种方式不推荐。
  4. (推荐)实现两个方法来保存/恢复状态信息
encodeRestorableStateWithCoder: 
  decodeRestorableStateWithCoder: 

另外,可以使用App 代理来存储版本等额外信息

application:willEncodeRestorableStateWithCoder: and application:didDecodeRestorableStateWithCoder:

三 State Restoration的流程

保存状态的流程图

恢复状态的流程图

从视图控制器的生命周期来看

awakeFromNib
 viewDidLoad
 decodeRestorableStateWithCoder:
 viewWillAppear
 viewDidAppear

如果是用代码实现的

+viewControllerWithRestorationIdentifierPath:coder:
 awakeFromNib
 viewDidLoad
 decodeRestorableStateWithCoder:
 viewWillAppear
 viewDidAppear

注意:比如v3->v2->v1,是层次结构,如果v2没有标识符,那么底层的v3即便有标识符也没办法恢复。一组层次结构要保存,一定要都有restorationIdentifier

四 Demo

项目链接
https://github.com/wenchenhuang/StateRestorationStoryboard

如何测试
在XCode中运行,然后Command+Shift+H进入后台,然后点击Stop的方框,然后再运行,会发现恢复了上次的状态

首先,在AppDelegate中实现两个代理函数

func application(application: UIApplication,shouldRestoreApplicationState coder: NSCoder) -> Bool {
        return true
    }
    func application(application: UIApplication,shouldSaveApplicationState coder: NSCoder) -> Bool {
        return true
    }

然后,看一下我的Storyboad上的架构

一个tabbarController,包括两个子Controller,然后第二个子Controller又包含一个ChildController

对每一个Storyboard上的vc都设置restoration identifiers,如图

对Tabbar当前选择哪个VC进行保存恢复

let KTABBARSELECTINDEX = "SelectIndex"
 class MainTabbarController:UITabBarController{ override func encodeRestorableStateWithCoder(coder: NSCoder) { let selectindex = self.selectedindex; coder.encodeInteger(selectedindex,forKey: KTABBARSELECTINDEX) //Do not forget to call super super.encodeRestorableStateWithCoder(coder) } override func decodeRestorableStateWithCoder(coder: NSCoder) { let selectindex = coder.decodeIntegerForKey(KTABBARSELECTINDEX) self.selectedindex = selectindex //Do not forget to call super super.decodeRestorableStateWithCoder(coder) } }

对FirstViewController的TextField内容进行保存/恢复

let KSECONDVCTEXTFIELD = "secondvctextField"
 class FirstViewController:UIViewController,UITextFieldDelegate{ @IBOutlet weak var textfield: UITextField! override func encodeRestorableStateWithCoder(coder: NSCoder) { coder.encodeObject(textfield.text,forKey: KSECONDVCTEXTFIELD) super.encodeRestorableStateWithCoder(coder) } override func decodeRestorableStateWithCoder(coder: NSCoder) { if let text = coder.decodeObjectForKey(KSECONDVCTEXTFIELD) as? String{ textfield.text = text } super.decodeRestorableStateWithCoder(coder) } func textFieldShouldReturn(textField: UITextField) -> Bool { textfield.resignFirstResponder() return true } }

对SecondViewController进行Textfield和ChildVC进行保存/恢复

let KFirsTVCTEXTFIELD = "firstvctextField"

class SecondViewController:UIViewController,UITextFieldDelegate {
    @IBOutlet weak var textfield: UITextField!
    override func encodeRestorableStateWithCoder(coder: NSCoder) {
        coder.encodeObject(textfield.text,forKey: KFirsTVCTEXTFIELD)
        //Parent vc should encode child vc,so that child vc can encode itself
        let childVC = self.childViewControllers.first as! UINavigationController
        coder.encodeObject(childVC,forKey: "childVC")
        super.encodeRestorableStateWithCoder(coder)
    }
    override func decodeRestorableStateWithCoder(coder: NSCoder) {
        if let text = coder.decodeObjectForKey(KFirsTVCTEXTFIELD) as? String{
            textfield.text = text
        }
        super.decodeRestorableStateWithCoder(coder)
    }
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textfield.resignFirstResponder()
        return true
    }
}

对嵌入式的TableViwController进行当前选择的行进行保存/恢复

let KTALBLESELECTEdindEX = "selectIndex"

class EmbedTableviewController:UITableViewController{

    override func encodeRestorableStateWithCoder(coder: NSCoder) {
        if let selectedindexs = self.tableView.indexPathsForSelectedRows(){
            //Encode select index
            var data = NSKeyedArchiver.archivedDataWithRootObject(selectedindexs)
            coder.encodeObject(data,forKey:KTALBLESELECTEdindEX)
        }
        super.encodeRestorableStateWithCoder(coder)
    }
    override func decodeRestorableStateWithCoder(coder: NSCoder) {

        var data = coder.decodeObjectForKey(KTALBLESELECTEdindEX) as? NSData
        if data != nil{
            //Decode select index
            if let selectedindexs = NSKeyedUnarchiver.unarchiveObjectWithData(data!) as? NSArray{
                for var i = 0;i < selectedindexs.count;i++ {
                    let oneIndex = selectedindexs[i] as! NSIndexPath
                    self.tableView.selectRowAtIndexPath(oneIndex,animated:false,scrollPosition: UITableViewScrollPosition.None)
                }
            }
        }
        super.decodeRestorableStateWithCoder(coder)
    }
}

Swift App状态恢复-State Restoration(一)的更多相关文章

  1. 详解如何通过H5(浏览器/WebView/其他)唤起本地app

    这篇文章主要介绍了详解如何通过H5(浏览器/WebView/其他)唤起本地app的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. H5混合开发app如何升级的方法

    本篇文章主要介绍了H5混合开发app如何升级的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. html5唤起app的方法

    这篇文章主要介绍了html5唤起app的方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. ios – 在新的Storyboard中将值传递给UIViewController – Swift

    我试图将值传递给新的视图控制器–位于新的故事板文件中.但是当我这样做时,我从NewViewController得到的结果总是为零.下面是我在新故事板中显示视图控制器的方法:我尝试在这里发送信息:然后呈现视图控制器.这是我的NewViewController,我尝试接收值.但最终仍然是零.我错误地发送了这些值吗?解决方法您正在使用此行创建NewViewController的新实例而是将变量和委托分配给已从StoryBoard实例化的vc

  5. ios – 将UIViewController视图属性设置为不带有storyboard / nib的自定义UIView类

    或者上面的代码片段是推荐的吗?

  6. ios – Xcode Storyboard中“推断”和“自由形式”有什么区别?

    我有一个自定义视图,需要在所有iPhone设备上显示.在创建自定义视图XIB时,我将其称为“推断”,但它没有为iPhone6和iPhone6Plus设备调整大小.我无法弄清楚这个问题.我对“推断”和“自由形式”之间的实际差异感到困惑.有人可以解释一下这些差异吗?

  7. xcode – 上传到App Store时进行身份验证

    只需为现有安装/文件夹创建备份,这很重要,因为在(新)安装期间,Transporter将删除以前的安装:现在运行以下命令来更新Transporter:希望这有助于某人.

  8. 在ios中将故事板从一个应用程序复制到另一个应用程序的最佳方法是什么?

    或者我在这里遗漏了什么?编辑-1我认为StoryBoard的布局大小仅限于iphone5.解决方法你这样做的方式是正确的.您只是遇到了布局约束不存在或错误的事实.关于故事板没有什么神奇之处……它只是一个像你复制到你的新项目中的任何其他文件.您应该在每个视图控制器中选择您的顶层视图,然后告诉Xcode删除所有约束并将它们重置为建议的约束,然后从那里开始.

  9. ios – Interface Builder无法确定“Main.storyboard”的类型.这可能是由于缺少SDK

    解决方法这没有你想象的那么复杂.该错误是因为您在Main.storyboard的源代码上输错了一些内容,例如我不小心在Main.storyboard的第一个打开标记之前放了一个“”.1)你必须做的是删除Main.storyboard2)转到垃圾箱,将Main.storyboard移动到桌面,用任何文本编辑器打开并修复代码;然后“全选”和“复制”.3)在Xcode上,创建一个全新的Main.storyboard,然后按右键单击文件并选择OpenAs–>源代码,然后粘贴您在剪贴板上的固定代码.4)右键单击文件

  10. 使用Xcode 6中的Storyboard的AppDelegate for Cocoa应用程序

    我有一个现有的OSX应用程序,在转换为Storyboards作为主界面后,我的app代理不再被使用.之前,MainMenu.xib有一个“AppDelegate”对象,我可以将它的类设置为我的appdelegate.但是,Storyboard不包含此类对象.如何恢复AppDelegate并保留故事板?

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部