Androidの左上ハンバーガーボタンのメニュー(NavigationView)っぽいレイアウトを極力Storyboardで実装してみた

最近似たようなレイアウト実装作業をしたため、試しに実装してみた。


##実装イメージ
![Androidハンバーガーメニュー.gif](https://qiita-image-store.s3.amazonaws.com/0/113982/536e0566-44fa-d6e8-41d3-156d41fbdf10.gif)

こんなの。
Androidのマテリアルデザイン用ライブラリみたいに下のレイアウトに
シャドウっぽいレイヤーをかける作業は省いのたで、
一旦見やすいようにメニュー用レイアウトに色をつけた。

左側でにょきにょきとレイアウトを変えるあたりのために入れた設定の備忘録。

##準備

実装イメージのようにナビゲーションバーの上にメニューをのっけたい場合、
メニューのレイアウトとその下のレイアウトは共にContainer Viewにのっける。

![スクリーンショット 2017-03-30 13.55.23.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/412985c3-55f6-b777-a8d3-b83f2a95b8df.jpeg)

こんな感じ。
中身に入れる画面はEmbedで繋いどく。
今回メニュー部分のレイアウトは左から生やすので、上・下・左辺は固定しとく。

![スクリーンショット 2017-03-30 14.03.08.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/bd5889cb-3506-3673-047a-41eef4191bc0.jpeg)


##メニューレイアウトの表示/非表示実装

###Storyboard

1．メニューレイアウトの幅設定を自ビューでも親ビュー比較でもいいので2個設定する。
　 1個は表示時、2個は非表示時。
　 初期表示で適用したい方のpriorityの設定値を高くする。以下キャプチャが設定例。

![スクリーンショット 2017-03-30 14.06.33.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/8a6c3501-3192-ccad-9f4e-2bdf58561c0a.jpeg)

【表示側】
![スクリーンショット 2017-03-30 14.11.55.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/edd4e716-0f54-6f48-6986-1e1e21d7b119.jpeg)

【非表示側】
![スクリーンショット 2017-03-30 14.20.04.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/fe7cd3fa-0988-dfd1-99fa-fd7bd873614b.jpeg)


2．上記のうち、どっちの設定でもいいのでIBOutlet設定をUIViewControllerへインポート。

![スクリーンショット 2017-03-30 14.16.45.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/61888afe-45c6-d244-afe4-6c57cc3e2fc7.jpeg)

3．メニューの開閉を行うボタンのIBAction設定を
　 Container View内のUIViewControllerへインポート。

![スクリーンショット 2017-03-30 14.25.02.jpg](https://qiita-image-store.s3.amazonaws.com/0/113982/3445afe2-b441-0663-87fe-b8e91175898a.jpeg)


###ソースコード
メニューレイアウトの開閉に必要なコーディングは、
Storyboardの設定手順2でインポートしたNSLayoutConstraintのpriorityの数字を変更して
layoutIfNeeded()を呼んで変更を画面に適用するだけ。
priorityの数字は変更後に1001以上にならなければ何でもいい。

以下のソースコードは今回のようなメイン画面もメニュー画面も
Container View入りの場合の例。


```swift:ViewController.swift
class ViewController: UIViewController {
    
    /// メニューレイアウトの幅
    @IBOutlet weak var _menuWidth: NSLayoutConstraint!
    
    /// メニュー表示フラグ
    var showMenu = false

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // メイン画面にこのクラスを渡して、メニューボタン押下時の処理を呼べるようにしておく
        let mainNavi = self.childViewControllers[0] as! UINavigationController
        let mainController = mainNavi.childViewControllers[0] as! MainViewController
        mainController.parentController = self
    }
    
    func changeMenuLayout() {
        if self.showMenu {
            // メニューを閉じる
            self.showMenu = false
            self._menuWidth.priority += 2 // width=0設定の優先度を上げて、メニュー非表示
        } else {
            // メニューを開く
            self.showMenu = true
            self._menuWidth.priority -= 2 // width=0設定の優先度を下げて、メニュー表示
        }
        // アニメーションさせつつメニューの開閉を切り替える
        UIView.animate(withDuration: 0.5, animations: {() -> Void in
            self.view.layoutIfNeeded()
        })
    }
}
```

changeMenuLayoutメソッドが処理今回目的とするレイアウト変更処理本体。
viewDidLoadでは子要素のContainer View内でchangeMenuLayoutを呼ぶ準備をしてるだけ。
準備した先で呼んでる処理が以下。

```swift:MainViewController.swift
class MainViewController: UIViewController {
    
    /// 親コントローラ
    var parentController: ViewController?

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    

    @IBAction func menuButtonDidTap(_ sender: UIBarButtonItem) {
        // メニューの開閉をする
        parentController?.changeMenuLayout()
    }
}
```


##備考
メニューの開閉に沿って今回下に表示していたメイン画面もスライドしたい、
メニューレイアウト分うまいこと縮小表示したい場合は
メニュー画面の右辺とメイン画面の左辺をEqual設定したりなどなど、
基本Storyboard上の静的設定の追加で対応可能。