iOSでの半モーダル/ハーフモーダルの実装についてのまとめ

## はじめに
- iOSのUIでハーフモーダルを見かけることが増えました。Apple純正のアプリでも実装されることが多く、今後実装することが多くなると思います。
今後の実装の参考になるように情報をまとめたいと思いました。

## 半モーダル/ハーフモーダルとは
- こちらの記事がとても参考になりますので引用します。[iOSにおける半モーダルビューの解釈](https://note.com/usagimaruma/n/n0c608c327e0c)

```
・モードの完全遷移が起こらない
・モードを多重化しながらパラレルに対話可能
・モードを終了させずにモードからの一時退避が可能
・擬似的なマルチウインドウ・インターフェイスに応用可能
・スワイプなどインタラクションコストの低い操作方法によってモードの切り替えが可能
```

- Pull dissmissや画面遷移せずにタスクを切り替える機能などによく使われます
- 代表的なものはAppleのMapアプリです

![Kapture 2020-11-30 at 16.59.16.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63985/7a5ea655-f36d-7c5e-5cb8-5d4fd6e9b60b.gif)


##　実装方法
## UIModalPresentationStyleを使う
![Kapture 2020-11-30 at 16.57.26.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63985/6984f40c-6819-36c6-e4a6-2a4cca8a3c3b.gif)

```swift
     let vc = UIViewController()
     vc.modalPresentationStyle = .pageSheet
     present(vc, animated: true, completion: nil)
```
- この実装だと下階層の画面に対してタップできないなどの仕様の制限があります。

## ライブラリを使う
- すでに完成度の高いライブラリが存在するので実装コストを下げることができます。

### [SCENEE/FloatingPanel](https://github.com/SCENEE/FloatingPanel)
- Apple純正のアプリのような動きを再現できます

![Kapture 2020-11-30 at 18.10.27.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63985/cae0195b-0938-a61f-cb18-6ea5e5fca45a.gif)

![Kapture 2020-11-30 at 18.12.03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63985/6c0fbef2-1763-8382-0034-0da04e7018a4.gif)

### [slackhq/PanModal](https://github.com/slackhq/PanModal)
![Kapture 2020-11-30 at 18.16.30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63985/71075b61-82ff-50f7-2013-6e517adf6b55.gif)


## 自前で実装する
- より完成度の高い完成度を目指す場合は自前で実装することも選択肢に入ります。実装する上でのポイントをあげておきます。

### 徹底調査
- 完成度の高いライブラリが多いので中身を覗いてみると理解が進みます。とくに上記で挙げたライブラリは参考になります。
- [cocoacontrols](https://www.cocoacontrols.com)などを使うとたくさんのライブラリを見つけることができます。

### UIViewControllerTransitioningDelegate
- この実装をすることで表示のアニメーションのカスタマイズと表示後の画面もカスタマイズできます

```swift
let vc = UIViewController()
vc.modalPresentationStyle = .custom
vc.transitioningDelegate = self
present(vc, animated: true, completion: nil)
```
```swift
extension UIViewController: UIViewControllerTransitioningDelegate {
    public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return UIPresentationController(presentedViewController: presented, presenting: presenting)
    }
    
    public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return PresentationAnimator()
    }
    
    public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DismissionAnimator()
    }
}
```

### PSPDFTouchForwardingView
- PSPDFTouchForwardingViewを使うことでModalで表示した下の階層のViewのタップイベントをとることができるようになります。
- [Presentation Controllers and Adaptive Presentations](https://pspdfkit.com/blog/2015/presentation-controllers/)

```swift
final class PSPDFTouchForwardingView: UIView {
    
    final var passthroughViews: [UIView] = []
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard let hitView = super.hitTest(point, with: event) else { return nil }
        guard hitView == self else { return hitView }
        
        for passthroughView in passthroughViews {
            let point = convert(point, to: passthroughView)
            if let passthroughHitView = passthroughView.hitTest(point, with: event) {
                return passthroughHitView
            }
        }
        
        return self
    }
}
```

```swift
private var touchForwardingView: PSPDFTouchForwardingView?
override func presentationTransitionWillBegin() {
    super.presentationTransitionWillBegin()
    touchForwardingView = PSPDFTouchForwardingView(frame: containerView!.bounds)
    containerView?.insertSubview(touchForwardingView, at: 0)
}
```

## まとめ
- 近年ハーフモーダルのUIを採用するアプリが増えてきている
- 完成度の高いライブラリも多く実装コストを下げることができる
- 自前実装もポイントを押さてしまえば簡単です🙆‍♂️


## 参考リンク
- https://engineering.mercari.com/blog/entry/2019-12-03-100000/
- https://pspdfkit.com/blog/2015/presentation-controllers/
- https://github.com/SCENEE/FloatingPanel
- https://note.com/usagimaruma/n/n0c608c327e0c