どこまでも分かりやすいスワイプ画面作成方法

<h1>はじめに</h1>
Google MapやFaceBookアプリのように、右側にスワイプするとレフト画面が表示されるUIを作成します。

2画面を重ねて表示させてい場合はスワイプ専用のViewを作成し、
上下にメイン画面(MainCV)とレフト画面(LeftCV)が表示されるよう設定します。

その際に使われるのが<em>Container View Controller</em>です。
今回は<em>Container</em>と操作に必要なパーツを乗せて、実装していきます。

<h1>手順</h1>
<ol>
 <li>Storyboardから必要なViewの配置</li>
 <li>ユーザーの操作設定</li>
 <li>Navigation Controllerの設定</li>
 <li>UIを制御するコードの記述</li>
</ol>
<h1>Storyboardから必要なViewの配置</h1>
<ol>
 <li>ContainerをObject Libraryから2つドラッグし、View Controllerへドロップする</li>
 <li>同様にViewをView Controllerへドロップする</li>
 <li>わかりやすくするために名前を変更する</li>
</ol>
![04.png](https://qiita-image-store.s3.amazonaws.com/0/83113/a65d21fd-6295-73e1-1bb1-32ab3c2e8bda.png)

今回は名前を以下のように設定しました。
<ul><li>Main CV → メイン画面を乗せるためのContainer View Controller</li><li>Left CV → レフト画面を乗せるためのContainer View Controller</li><li>Cover View →  画面を移動させたときに上の画面を覆うView</li></ul>

現在のエディタエリアは以下のようになっています。
わかりやすいようにViewに色をつけています。
![02.png](https://qiita-image-store.s3.amazonaws.com/0/83113/73622aa5-0116-9240-3fe7-1181b0687940.png)

このままではCover Viewが全体を覆っているため、後ろのUIが表示できません。
そこで
<ol><li>Document OutlineからCover Viewを選択</li><li>Editor EreaのAttribute InspectorからViewのBackgroundをClear Colorに設定</li><li>User Interaction Enabledのチェックを外す</li></ol>
![05.png](https://qiita-image-store.s3.amazonaws.com/0/83113/389c527d-417c-05fb-098b-aab608d21d56.png)
必要なViewの配置は以上です。
<h1>ユーザーの操作設定</h1>
次に、ユーザーの操作に反応させていきます。
タップやスワイプのイベントに反応するために<em>Gesture Recognizer</em>を配置していきます。
<ol><li>Tap Gesture RecognizerをObject Libraryからドラッグし、Document OutlineのCover Viewへドロップする</li><li>同様にObject LibraryからSwipe Gesture Recoginizerを2個Viewへドロップする</li><li>わかりやすくするために名前を変更する</li></ol>
![08.png](https://qiita-image-store.s3.amazonaws.com/0/83113/18c34cb7-588f-5d6e-f847-e3a8c3f98a1a.png)

Swipe Gesture Recoginizerは初期状態で右へ動くよう設定されています。
レフト画面からメイン画面に戻りたい場合、右側にあるメイン画面を左側にスライドさせたいので、
<ol><li>Document OutlineのLeftSwipedを選択し、Attribute InspectorからSwipeをLeftに設定する</li></ol>
![09.png](https://qiita-image-store.s3.amazonaws.com/0/83113/39239d37-6272-a1f2-a252-bc0edadc69b9.png)
これで、Swipeの設定は以上です。
<h1>Navigation Controllerの設定</h1>
Container View ControllerとView Controllerの間にNavigation Controllerを設置します。
<ul><li>Document OutlineからLeft VCを選択</li><li>ツールバーからEditorを選択し、Embed In → Navigation Controllerをクリック</li></ul>
![10.png](https://qiita-image-store.s3.amazonaws.com/0/83113/4b3b46b2-cd5e-8e44-a348-30c5371cdb8d.png)

Editor Ereaは以下のようになります。
![11.png](https://qiita-image-store.s3.amazonaws.com/0/83113/4c26488c-1e02-d499-ef20-0e66cdd7454d.png)

今回、レフト画面はメニューを表示させたいので、Table View Controllerに変更します。
<ol><li>Object LibraryからTable View Controllerを適切な場所へドラッグ&ドロップ</li><li>Navigation ControllerからTable View Controllerへセグエを繋げる</li><li>不必要になったグレー背景のView Controllerを削除</li></ol>

ユーザーの操作設定は以上です。

<h1>UIを制御するコードの記述</h1>
UI要素をコードへ接続していきます。
<h2>現在のView名をわかりやすくする</h2>
ソースコードを実装する前に、現在のView名をわかりやすくしましょう。

![14.png](https://qiita-image-store.s3.amazonaws.com/0/83113/b2e3b91f-d832-54f7-7714-ad21e4dc625e.png)
![15.png](https://qiita-image-store.s3.amazonaws.com/0/83113/92a383ab-593e-8859-d5c1-50ec602456a3.png)

<h2>各Viewにクラスを指定</h2>
今回は3つのviewを使用しているので、3つクラスを用意します。
既にあるViewController.swiftはContainerVCの処理を記述したいので、新しくMainVC.swiftとLeftTV.swiftを作成します。
<ul><li>Container VC → ViewController.swift(既存のものを使用)</li><li>Main VC → MainVC.swift</li><li>LeftTV → LeftTV.swift</li></ul>
![20.png](https://qiita-image-store.s3.amazonaws.com/0/83113/484dd0a3-cc74-6992-7f34-9a2eee06ad0d.png)

作成したらインスペクタペインからそれぞれのクラスを対応させましょう。

<h2>View Controllerへ接続</h2>
まずはView ControllerにMainstoryboardのDocument Outlineから
<ul><li>Main CV</li><li>Left CV</li><li>Cover View</li></ul>
を接続させます。

```swift:ViewController
@IBOutlet weak var MainCV: UIView!
@IBOutlet weak var LeftCV: UIView!
@IBOutlet weak var CoverView: UIView!
```
次にスワイプした際に反応するよう、
<ul><li>Tap Gesture Recognizer</li><li>LeftSwpied</li><li>RightSwpied</li></ul>
を記述します。

```swift:ViewController
    @IBAction func coverTapped(sender: AnyObject) {
    }
    @IBAction func leftSwiped(sender: AnyObject) {
    }
    @IBAction func rightSwiped(sender: AnyObject) {
    }
```
スワイプした際に動く範囲やアニメーション方法を指定したいので、メソッドを指定します。
<ul><li>showBackground() → LeftTV画面を表示させるために右に画面をズラすメソッド</li><li>hideBackground() → MainVC画面を再表示させるために左に画面を戻すメソッド</li></ul>

```swift:ViewController
    @IBAction func coverTapped(sender: AnyObject) {
        hideBackground()
    }
    @IBAction func leftSwiped(sender: AnyObject) {
        hideBackground()
    }
    @IBAction func rightSwiped(sender: AnyObject) {
        showBackground()
    }
```

追加したプロパティで、LeftTV画面が見えているときに固定させます。

```swift:ViewController.swift
    var isBackgroundVisible = false
```


```swift:ViewController
    func showBackground() {
        if (self.isBackgroundVisible) { return }
        self.isBackgroundVisible = true
        
        UIView.animateWithDuration(0.3, animations: { () -> Void in
            self.MainCV.frame = CGRectOffset(self.CoverView.frame, 230, 0)
            }, completion: nil)
        self.CoverView.frame = CGRectOffset(self.CoverView.frame, 230, 0)
        self.CoverView.userInteractionEnabled = true
    }
    
    func hideBackground() {
        if (!self.isBackgroundVisible) { return }
        self.isBackgroundVisible = false
        
        UIView.animateWithDuration(0.3, animations: { () -> Void in self.MainCV.frame = CGRectOffset(self.CoverView.frame, -230, 0)}, completion: nil)
        self.CoverView.frame = CGRectOffset(self.CoverView.frame, -230, 0)
        self.CoverView.userInteractionEnabled = false
    }

```
<h2>MainVCとLeftTVにも接続</h2>

最後にMainVCとLeftTVにもshowBackgroundMenu関数とhideBackgroundMenu関数を記述します。

```swift:MainVC.swift
    func showBackgroundMenu() {
    }
    
    func hideBackgroundMenu() {
    }
```


```swift:LeftTV.swift
    func showBackgroundMenu() {
    }
    
    func hideBackgroundMenu() {
    }
```

<h2>ソースコード一覧</h2>

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var MainCV: UIView!
    @IBOutlet weak var LeftCV: UIView!
    @IBOutlet weak var CoverView: UIView!
    
    @IBAction func coverTapped(sender: AnyObject) {
        hideBackground()
    }
    @IBAction func leftSwiped(sender: AnyObject) {
        hideBackground()
    }
    @IBAction func rightSwiped(sender: AnyObject) {
        showBackground()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let rightGesture = UISwipeGestureRecognizer(target: self, action: "rightSwiped:")
        let leftGesture = UISwipeGestureRecognizer(target: self, action: "leftSwiped:")
        let tapGesture = UITapGestureRecognizer(target: self, action: "coverTapped:")
        leftGesture.direction = .Left
        self.view.addGestureRecognizer(rightGesture)
        self.view.addGestureRecognizer(leftGesture)
        CoverView.addGestureRecognizer(tapGesture)
    }
    
    var isBackgroundVisible = false
    
    func showBackground() {
        if (self.isBackgroundVisible) { return }
        self.isBackgroundVisible = true
        
        UIView.animateWithDuration(0.3, animations: { () -> Void in
            println(self.CoverView.frame)
            self.MainCV.frame = CGRectOffset(self.CoverView.frame, 230, 0)
            }, completion: nil)
        self.CoverView.frame = CGRectOffset(self.CoverView.frame, 230, 0)
        self.CoverView.userInteractionEnabled = true
    }
    
    func hideBackground() {
        if (!self.isBackgroundVisible) { return }
        self.isBackgroundVisible = false
        
        UIView.animateWithDuration(0.3, animations: { () -> Void in
            self.MainCV.frame = CGRectOffset(self.CoverView.frame, -230, 0)
            }, completion: nil)
        self.CoverView.frame = CGRectOffset(self.CoverView.frame, -230, 0)
        self.CoverView.userInteractionEnabled = false
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func todoListVCDidTapeditButton(VC: MainVC) {
        self.showBackground()
    }
    func leftViewDidSelectCell(cellNumber: Int) {
        self.hideBackground()
    }
}
```

AutoLayoutでcoverViewがズレているとへんな動きになるので、AutoLayoutの指定を間違えないようにしてくださいね。

以上です。