Swift 学习笔记5 - Segue & View C

作者: 诸葛俊伟 | 来源:发表于2016-05-15 07:51 被阅读2548次

    前言

    这是斯坦福大学的课程-Developing iOS 9 APPs with Swift,第6节课的内容。主要是讲:组合多个 MVC (Multiple MVCs) 和 View Controller 的生命周期。在 Swift 学习笔记3中我也提到过一些 Multiple MVCs 的基础知识,这篇文章算是应用进阶版。

    经过这段时间的学习,个人总结了3个非常好的学习 swift 基础的途径资料(列在下方)。其它的资料比如各路大神的书籍和博客我作为辅助学习,比如喵神YYKit 的作者 ibireme等等。进阶的资料,我觉得学习 Github 中的各种开源项目是个非常好的选择,比如喵神的 Kingfisher 等。当然如果大家能订阅我的博客,我会非常开心哒。:)

    下面是我用的最多的3个 Swift 基础学习资料:

    • 斯坦福的这个课程,Developing iOS 9 Apps with Swift.
    • 苹果官方文档,里面也有实例教程Learn by doing 永远是最好的学习方法。
    • 按住 option 点击代码中的任一单词。这个大家应该都知道,但我是真心越来越觉得这个太好用了。

    Demo

    又是这张萌蠢的脸,但这次它不仅换了发型,还添加了按钮。感兴趣的童鞋可以去我的 Github 查看源码。

    Segue

    首先介绍一个名词 - segue。简短的翻译就是:转换。

    我们创建了很多 Controller 的 Controller,我们需要用一个 MVC 触发另一个 MVC,这种 MVC 之间的转换就是 segue,一般不用 transition,而用 segue,之后会经常遇到。

    主要有4种 segue:

    • Show Segue(比如在 Navigation Controller 中,一个 MVC 出现在另一个 MVC 里。)
    • Show Detail Segue(比如我们这次的 Demo,一个 master,一个 detail,那么就需要用到这个 show detail。Navigation Controller 中也可以使用 show detail segue,和 show segue 功能一样。)
    • Modal Segue(占据整个屏幕)
    • Popover Segue(出现一个小弹窗)

    Segue 会创建一个新的实例(后文也会继续提到)。就是说每次点击同样的按钮,它返回的不是之前的那个界面,虽然长的也许一样,但其实是一个全新的界面,是重新创建的。

    必须要给新建的 segue 一个 Identifier(在 Attributes inspector 中设置)。每个 segue 都要有自己独特的 id,因为我们要对它进行操作。比如我们可以使用 UIViewController 的方法,func performSegueWithIdentifier(identifier: String, sender: Anyobject?) 启用 segue,但我们几乎不用这种方法,一般都在storyboard 中直接用 ctrl 拉线。segue 的 id 更重要的一个用途是:preparing for a segue。用代码来说就是:

    func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // sender 一般是 button,但可以是任何东西
      if let identifier = segue.identifier { // 检查是否 nil
        switch identifier {
          case "Show Graph":
            if let vc = segue.destinationViewController as? GraphController { // 如果非 nil,则作为 GraphController
              vc.property1 = ...
              vc.CallMethodToSetItUp(...)
            }
          default: break
        }
      }
    }
    

    在上面的这段代码中,有个 switch case 语句,而我们在 Demo 中,充分发挥了 Swift 的特性,使用了字典,使得我们的代码更加优雅(这也是我为什么喜欢 Swift 的原因,相比于 CPP)。

    private let emotionalFaces: Dictionary<String,FacialExpression> = [
    

    注意:这个准备(preparation)的过程是在设置好 outlet 之前完成的,其实也挺符合实际,因为先把该准备的东西准备好了,才能去设置再去呈现。但在实际开发过程中很容易出现 bug,不自觉的就会去使用未设置好的 optional 变量。文末会介绍这样的 bug 以及相应的处理对策。

    我们也可以阻止 segue,只要在 UIViewController 中实现下面这个方法,返回 false。

    func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool
    

    组合多个 MVC

    有3种方式可以将多个 MVC 联合起来,

    1. Tab Bar Controller:
      每个界面应该是相互独立的,因为如果两个界面相互关联,如上面提到的我们这次的 Demo,我们希望用户点击了某个相应的事件按钮才能看到下个界面,而不是直接点了和事件无关的 Tab Bar 就能看到下个界面。所以我们的 Demo 不能用 Tab Bar Controller。
    2. Split Controller:
      如我在 Swift 学习笔记3 - Gesture中有提到的, “对于一个split view来说,[0]是主体部分, [1]是细节部分”。对于我们这次的 Demo,带有 “Angry”, "Worried" 等按钮的界面可以是主体部分 (master),而那个呆萌的脸可以作为呈现细节的另一个view (detail)。 别忘记 Split Controller 是用于 ipad 的(以及6 plus),那是因为 6 plus 以前的 iphone 屏幕不够大,没办法 split。要想在 6 里用 split,我们还是需要借助下面的这个哥们,Navigation Controller.
    3. Navigation Controller:
      选中主体部分(master),然后 Editor -> Embed in -> Navigation Controller。Boom!It worked! 其实它就是多了个 Back 按钮,这也是 Navigation Controller 精髓所在。在之前的学习笔记中也说过,Navigation Controller 是通过 stack 的方式存放这些 view 的,Back 就代表将现在的这个 view 抛弃掉(是彻底抛弃哦,重新点击按钮,它创建的就是一个新的界面,和之前的那个毛线关系没有啦),然后返回上一个 view。但如果是 Tab Bar,它就是一直存在的,也就是说点击一个 tab A,再点 B,再点 A,它出现的还是原来的 A。用教授的话说就是 “tab bar 不是 segue,navigation 是 segue”。
    • 有一点需要注意的是,如果在一个 Navigation Controller 中放有另一个 Navigation Controller,iOS 会自动忽视里面的那个 Navigation Controller。
    • 当我们想在 detail 的那个界面加个标题,我们还需要在那个 detail 上 embed 一个 Navigation Controller,但是这样的话在 ipad 上运行那些按钮就没用了,因为 prepare 的是 UINavigationController, 而我们想要 prepare 的是 UINavigationController 里面的内容,也就是我们之前做好的 detail view 里的东西。所以我们还需要加上这段代码,也就是 prepare UINavigationController 里面的内容的。
    if let navcon = destinationvc as? UINavigationController {
                destinationvc = navcon.visibleViewController ?? destinationvc
            }
    

    View Controller 生命周期

    生命周期大概是这样的:

    • Creation. 一般在 storyboard 中创建实例。
    • Preparation if being segued to.
      如之前所说,这个准备过程在 Outlet Setting 之前完成。
    • Outlet Setting.
    • Appearing and disappearing.
    • Geometry changes.
    • Low-memory situation. 一般在现在的 iphone 中很少发生。但要是发生了,可以释放好久不用的占用很大空间的事件,比如最长未使用原则(LRU)。

    在 storyboard 中创建实例,设置了 outlet 之后,就是调用 viewDidLoad 方法了。viewDidLoad, viewWillLoad, viewWillAppear, viewWillDisappear, viewDidAppear, viewDidDisappear 等等这么多方法,我会在下面逐条整理。

    • viewDidLoad

      view 只会 load 一次,一般在这里在这里进行一些初始化的工作,我们一般不用 init 方法,因为此时 outlet 已经设置完毕了。我们一般也将 update UI 的工作放在这个方法里。但是 view 的 geometry 不在这里设置,因为还不知道使用的设备是什么。这里的 geometry 的意思是 view 的大小尺寸,横向还是纵向之类的。
    override func viewDidLoad() {
     super.viewDidLoad() 
     // 进行一些 MVC 初始化的工作
    }
    
    • viewWillAppear

      这是 view 即将呈现出来之前的方法,一般把需要大量运算的程序放在这里(比如多线程的操作),view 的 geometry 也是在这里设置的,但是如果要做旋转之类的操作,其它地方会响应这些操作。
    override func viewDidLoad() {
     super.viewDidLoad() 
     // 进行一些 MVC 初始化的工作
    }
    
    • viewDidAppear

      这是在 view 呈现出来之后的方法,一些动画在这里进行。
    func viewDidAppear(animated: Bool)
    
    • viewWillDisappear

      这是 view 即将消失之前的方法,主要做一些简单的清理工作,但一些非常耗时的工作不是在这里进行的,这里可以关闭动画。
    override func viewWillDisappear(animated: Bool) {
     super.viewWillDisappear(animated) // 在所有的 viewWill/Did 方法中,调用 super。
     // 进行一些 MVC 初始化的工作
    }
    
    • viewDidDisappear

      这是在 view 消失之后的方法,释放一些在 willappear 阶段从网络上拿来的数据。
    func viewDidDisappear(animated: Bool)
    
    • func viewWillLayoutSubviews()func viewDidLayoutSubviews(),这两个方法用于几何变换的时候(geometry),而我们在 storyboard 中相对应于那些蓝线设置的 constraints,是在这两个方法之间发生的。这里其实我们不需要做什么特殊操作,因为都是自动完成的。
    • viewWillTransitionToSize

      在做旋转变换的时候,就是将手机或者 pad 屏幕横过来的时候,我们可以在这里设置一些动画属性。
    func viewWillTransitionToSize() {
      size: CGSize,
      withTransitionCoordinator: UIViewControllerTransitionCoordinator
    }
    
    • awakeFromNib

      在 preparation 和 outlet set 之前(在 MVC load 之前)。这个会发给任何对象,不只是 ViewController。

    概括这个周期,就是:

    • Instantiated (一般从 storyboard 中创建实例)
    • awakeFromNib
    • segue preparation
    • outlet set
    • viewDidLoad

      以下这些会经常调用,比如每次显示和关闭某个 view 的时候
    • viewWillAppear & viewDidAppear
    • viewWillDisappear & viewDidDisappear

      以下几何变换的方法有可能在 viewDidLoad 之后的任何时候被调用
    • viewWillLayoutSubviews
    • 自动部署布局
    • viewDidLayoutSubviews
    • didReceiveMemoryWarning (内存不够的时候)

    Demo 中的 bug 和对策

    有个 bug 在 iOS 开发中应该会经常遇到,就是

    fatal error: unexpectedly found nil while unwrapping an Optional value
    

    如 Paul Hegarty 教授所讲,

    Your outlets are not set at the time you preparing

    这是因为有个 optional 我们没有处理,比如在这个 demo 中,faceView 是 optional 的,faceView 是这样来的:

    @IBOutlet weak var faceView: FaceView!
    

    所以在后面 update faceView 的时候,需要考虑它 nil 的情况,有两种措施:

    • 第一种方法,以其中一条语句为例
    faceView?.eyeBrowTilt = eyeBrowTilts[expression.eyeBrowns] ?? 0.0
    

    注意等号左边的问号 faceView?,它可以处理 optional nil 的问题,如果式子的任何一个地方为 nil,那么它就会 ignore 整条语句。但是这样的语句很多,我们不能每条都这样处理。仅管我们是码农程序猿,我们也要学会优雅。:)

    • 另一个方法就是加上 if faceView != nil { },这样就避免了 nil 问题了。

    by:诸葛俊伟
    欢迎转载,转载请注明出处。访问我的个人主页,了解更多。

    相关文章

      网友评论

        本文标题:Swift 学习笔记5 - Segue & View C

        本文链接:https://www.haomeiwen.com/subject/cbdvrttx.html