Material Components for iOS のFlexibleHeaderを使ってみた

# Material Components for iOS
Material ComponentsはGoogleが提供している、Material Designの実装を助けてくれるコンポーネントライブラリです。
詳しくは、次のページを読みましょう
https://github.com/material-components/material-components-ios

## FlexibleHeaderとは
Google純正アプリの詳細画面などでみられる、スクロールに合わせヘッダー部分の高さが動的に変わるデザインに用いられています。

Flexible header
https://material.io/develop/ios/components/flexible-headers/

Swifterの方は、次のShrineというDemoアプリを参考にするのがおすすめです。
https://github.com/material-components/material-components-ios/tree/develop/demos/Shrine

## StoryBoardを使って実装して見る
手順は次のようになります。

- 画面全体のContainerとなるViewContorllerを作る
   - MDCFlexibleHeaderContainerViewControllerを継承した、UIViewContorllerを作る
- ContentとなるViewContorllerを作る
   - Headerを除いた部分、TableViewやCollectionViewなどScrollViewを実装したUIViewContorllerを作る
- HeaderとなるViewを作る

<img width="375" alt="Simulator Screen Shot - iPhone 8 - 2018-05-13 at 22.44.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/55153/dc79abcd-3fe4-2ee9-3ada-9f557b45c158.png">


## 画面全体のContainerとなるViewContorllerを作る
MDCFlexibleHeaderContainerViewControllerを継承したViewControllerを作ります。
StoryBoardから生成することを想定しているので、viewDidLoad()で初期セットを行なっています。getContentViewController()は後ほど作成する、「ContentとなるViewContorller」です。


MDCFlexibleHeaderContainerViewControllerのプロパティ：contentViewControllerにセットします。


```swift
class FlexibleHeaderViewController: MDCFlexibleHeaderContainerViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Content部分のViewControllerをセット
        let contentVC: ContentViewController = getContentViewController()
        self.contentViewController = contentVC
        // Header部分のViewを制御するため、headerViewControllerをContentにセット
        contentVC.headerViewController = self.headerViewController
        contentVC.setupHeaderView()
    }
}
```


## ContentとなるViewContorllerを作る
ContentとなるViewControllerは、HeaderのViewとそれを制御するためのMDCFlexibleHeaderViewControllerを参照できるようにします。

```swift
class ContentViewController: UITableViewController {
    var headerViewController: MDCFlexibleHeaderViewController?
    var headerContentView: HeaderView =  HeaderView.instantiateFromNib()
}
```

Headerのスクロールを連動させるために、スクロールのDelegateであるscrollViewDidScrollをつなげます。

```swift
    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        headerViewController!.scrollViewDidScroll(scrollView)
    }
```

今回の例では「画面全体のContainerとなるViewController」からコールしていますが、Header部分のViewのセットです。HeaderのViewのセットと、その内容となるViewをセットしています。

```swift
    func setupHeaderView() {
        let headerView = headerViewController!.headerView
        // ヘッダー部分のサイズセット
        headerView.trackingScrollView = self.tableView
        headerView.maximumHeight = 200
        headerView.minimumHeight = 72
        headerView.minMaxHeightIncludesSafeArea = false
        headerView.backgroundColor = UIColor.blue
        headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        // ヘッダー部分のコンテンツセット
        headerContentView.frame = (headerView.bounds)
        headerContentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        headerView.addSubview(headerContentView)
    }
```

## HeaderとなるViewを作る
Header部分に表示するViewを作成しましょう。

# デメリット
次の公式ページにも説明がありますが、UINavigationControllerとの併用する場合、NavigationBarを自前で制御しないといけません。

https://material.io/develop/ios/components/flexible-headers/

スクロール操作できるスペースにも注意が必要です。Header部分を縦スクロールしても動作しません。この辺り、TableViewのみで実装した場合と操作感が変わってしまう恐れがあります。