iOS9のPeek and Pop時のUIViewControllerのライフサイクル

#Peek and Pop
iOS9で追加された3D Touchの機能のひとつ
3D TouchやPeek and Popの詳細は以下の記事を参照
[3D Touch 概要](http://qiita.com/usagimaru/items/e4fb2ba8d8a7ae96b446)
#Peek and PopのときのUIViewControllerのライフサイクル
Peek and Popを使用しているときの遷移前と遷移先のUIViewControllerのライフサイクルがどうなっているのか気になったので調査してみました。

##コード
###ViewController：最初の画面
```swift:ViewController
import UIKit

// 最初の画面
class ViewController: UIViewController , UIViewControllerPreviewingDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        print("ViewController viewDidLoad")        
        
        // self.viewに対してPeek and Popを登録
        registerForPreviewingWithDelegate(self, sourceView: self.view)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    // 少し力を加えた段階で呼ばれる
    func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        print("ViewController previewingContext location")

        // 遷移後の画面
        return SecondViewController()
    }
    
    // Popの段階で呼ばれる
    func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
        print("ViewController previewingContext viewControllerToCommit")
        showViewController(viewControllerToCommit, sender: self)
    }
    
    override func loadView() {
        super.loadView()
        print("ViewController loadView")
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        print("ViewController viewWillAppear")
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        print("ViewController viewWillDisappear")
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        print("ViewController viewDidAppear")
    }
    override func viewDidDisappear(animated: Bool) {
        super.viewDidAppear(animated)
        print("ViewController viewDidDisappear")
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        print("ViewController viewWillLayoutSubviews")
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        print("ViewController viewDidLayoutSubviews")
    }    
}
```
###SecondViewController：遷移後の画面
```swift:SecondViewController
import UIKit

// 遷移後の画面
class SecondViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("SecondViewController viewDidLoad")
    }
    
    override func loadView() {
        super.loadView()
        print("SecondViewController loadView")
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        print("SecondViewController viewWillAppear")
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        print("SecondViewController viewWillDisappear")
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        print("SecondViewController viewDidAppear")
    }
    override func viewDidDisappear(animated: Bool) {
        super.viewDidAppear(animated)
        print("SecondViewController viewDidDisppear")
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        print("SecondViewController viewWillLayoutSubviews")
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        print("SecondViewController viewDidLayoutSubviews")
    }    
}

```

##ライフサイクル
[UIViewControllerのライフサイクル](http://qiita.com/mo_to_44/items/0ca628b4cc74c8c5599d)

###1.起動時
起動時は通常のライフサイクルと同じなので特に問題なし

```
ViewController loadView
ViewController viewDidLoad
ViewController viewWillAppear
ViewController viewWillLayoutSubviews
ViewController viewDidLayoutSubviews
ViewController viewDidAppear
```
###2.通常の画面遷移（presentViewControllerでの遷移）
```
SecondViewController loadView
SecondViewController viewDidLoad
ViewController viewWillDisappear
SecondViewController viewWillAppear
SecondViewController viewWillLayoutSubviews
SecondViewController viewDidLayoutSubviews
ViewController viewWillLayoutSubviews
ViewController viewDidLayoutSubviews
SecondViewController viewWillLayoutSubviews
SecondViewController viewDidLayoutSubviews
SecondViewController viewDidAppear
ViewController viewDidDisappear
```

###3.少しだけ押した時
（`func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?`
が呼ばれるとき）

```
ViewController previewingContext location
SecondViewController loadView
SecondViewController viewDidLoad
SecondViewController viewWillAppear
SecondViewController viewWillLayoutSubviews
SecondViewController viewDidLayoutSubviews
SecondViewController viewWillLayoutSubviews
SecondViewController viewDidLayoutSubviews
```

この状態で指を離した場合

```
SecondViewController viewWillDisappear
SecondViewController viewDidDisppear
```

という感じで圧力を検知した時点でViewControllerの表示の準備されてすぐに表示できるような状態になっています。

###4.Peek時（プレビューが表示される時）

```
SecondViewController viewDidAppear
```
この状態で指を話した場合

```
SecondViewController viewWillDisappear
SecondViewController viewDidDisppear
```

という感じで

* PeekになったらviewDidAppearが呼ばれます。
* この時点での通常の画面遷移との違いは遷移後のSecondViewControllerのviewDidAppearが呼ばれていても遷移前のViewControllerのviewWillDisappearやviewDidDisappearは呼ばれていません。(遷移前、遷移後両方のUIViewControllerがviewDidAppearの状態）

###5.Pop時（ViewControllerが遷移する時）

```
SecondViewController viewWillDisappear
SecondViewController viewDidDisppear
ViewController previewingContext viewControllerToCommit
ViewController viewWillDisappear
SecondViewController viewWillAppear
SecondViewController viewDidAppear
ViewController viewDidDisappear
SecondViewController viewWillLayoutSubviews
SecondViewController viewDidLayoutSubviews
```

という感じで

* Popになると一度PeekでよばれたviewDidAppearがキャンセルされてviewWillDisappear, viewDidDisappearが呼ばれます。
* その後Popになるときに呼ばれる`previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController)`が呼ばれて画面遷移の処理が行われます。
* Popの時には遷移前のViewControllerのviewWillDisappearやviewDidDisappearがちゃんとよばれます。
* 通常の画面遷移と異なる点は遷移後のSecondViewControllerのviewWillLayoutSubviews, viewDidLayoutSubviewsが呼ばれるタイミングがviewDidAppearよりもあとに呼ばれるようになっています。

##まとめ
Peek and Popを使用しているときは通常の画面遷移時とは遷移前、遷移後のUIViewControllerのライフサイクルが異なるようなので注意しましょう。