【UIKit】ハーフモーダルのやり方

# はじめに
いつかやってみたかったハーフモーダルの実装、
ドキュメントや記事を見る限り、思ったより簡単？そうに思えたのでやってみました。

# 整理
### モーダルの表示
表示: `親画面.present(子画面:animated:completion:)`
遷移スタイル: `UIModalTransitionStyle.coverVertical`※デフォルト
表示スタイル: `UIModalPresentationStyle.automatic`※デフォルト
👉 　`子画面.modalPresentationStyle = .popover`に変更する

### モーダルの設定
`UISheetPresentationController`を取得、`detents`を変更する

ポップオーバー：`子画面.popoverPresentationController`
シート：`ポップオーバー.adaptiveSheetPresentationController`


ディテント（戻り止め): `UISheetPresentationController.detents = [.large()]`※デフォルト
👉 `シート.detents = [.medium(), .large()]`に変更してハーフモーダル可にする
👉 `シート.detents = [.medium()]`ハーフモーダルのみの場合

##### 条件により詳細設定
■ ハーフモーダル表示中に子画面自体をスクロールさせる（フルモーダル化はエッジ引き上げ）
👉`シート.prefersScrollingExpandsWhenScrolledToEdge = false`にする


■ 特定のイベント後にハーフモーダルにする場合👉

```swift
シート.animateChanges {
    シート.selectedDetentIdentifier = .medium
}
```

■  ハーフモーダル表示中に親画面も操作可能にさせる
👉`シート.largestUndimmedDetentIdentifier = .medium`にする

■ （デバイス横向き時など）モダールが画面いっぱいに表示され困る場合
👉`シート.prefersEdgeAttachedInCompactHeight = true`にする
※デフォルトはfalse

さらに横幅を指定したい場合は、
`シート.widthFollowsPreferredContentSizeWhenEdgeAttached = true`
※デフォルトはfalse
にすることで`子画面.preferredContentSize`の値でカスタマイズできる

■ 他にもモーダルの角を丸くしたり、上部につまみを付けたりできる
角の丸み： `シート.preferredCornerRadius = 20.0` ※デフォルトはnil
つまみ：　`シート.prefersGrabberVisible = true` ※デフォルトはfalse

### モーダルのクローズ
クローズ:　`dismiss(animated:completion:)`

# 実装
親画面でモーダル表示の設定を行う

```swift:MainViewController
    func showHalfModal() {
        let storyboard = UIStoryboard(name: "HalfModal", bundle: nil)
        let vc = storyboard.instantiateViewController(identifier: "HalfModal")
        guard let modalVC = vc as? HalfModalViewController else { return }
        modalVC.delegate = self

        // スタイルをポップオーバーに設定
        modalVC.modalPresentationStyle = .popover
        if let popover = modalVC.popoverPresentationController {
            // iOS 15.0から設定可能なので
            if #available(iOS 15.0, *) {
                let sheet = popover.adaptiveSheetPresentationController
                // ハーフモーダルのみに設定
                sheet.detents = [.medium()]

                // 以下オプションの設定
                sheet.prefersScrollingExpandsWhenScrolledToEdge = false
                sheet.prefersEdgeAttachedInCompactHeight = true
                sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
            }
        }
        // モーダル表示
        self.present(modalVC, animated: true)
    }
```



**公式ドキュメント書き方はこんな感じで`viewController`から直接`sheet`を取ってた**

```swift
func showMyViewControllerInACustomizedSheet() {
    let viewControllerToPresent = MyViewController()
    if let sheet = viewControllerToPresent.sheetPresentationController {
        sheet.detents = [.medium(), .large()]
        sheet.largestUndimmedDetentIdentifier = .medium
        sheet.prefersScrollingExpandsWhenScrolledToEdge = false
        sheet.prefersEdgeAttachedInCompactHeight = true
        sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
    }
    present(viewControllerToPresent, animated: true, completion: nil)
}
```

# 感想
実装が簡単で感動しました。
実際に個人開発中のアプリでハーフモーダルを実装していじってみましたが、操作性がアップした気がします。
今後は可能な限りハーフモーダル（もしくはハーフ&フル）に切り替えていこうと思います。

# 参考記事
https://developer.apple.com/documentation/uikit/uiviewcontroller/customize_and_resize_sheets_in_uikit

https://developer.apple.com/videos/play/wwdc2021/10063/

https://developer.apple.com/documentation/uikit/uisheetpresentationcontroller

https://qiita.com/akatsuki174/items/152ba0848dbd375dae4e