はじめに
WWDC2021 で発表のあったハーフモーダルを実装することができる UISheetPresentationController を使ってみたので、
使用方法と使ってみた感想などを綴っておきたいと思います。
これまでは PanModal や FloatingPanel 等のライブラリを使って実装していましたが、
公式でもサポートされたようなので使い勝手を調べてみました。
環境
Xcode: 13.0 beta 4 (13A5201i)
Swift: Swift5
iOS: iOS15(public beta 4)
使用方法
以下のようにするだけで簡単にモーダル画面を実装することができます。
final class ViewController: UIViewController {
private lazy var button: UIButton = {
let button = UIButton()
button.setTitle("open", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.layer.cornerRadius = 8.0
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 30.0),
button.widthAnchor.constraint(equalToConstant: 60.0)
])
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
button.addTarget(self, action: #selector(tapButton), for: .touchUpInside)
}
@objc
private func tapButton() {
let vc = ModalViewController(nibName: nil, bundle: nil)
/* ここが実装のキモ */
if let sheet = vc.sheetPresentationController {
// ここで指定したサイズで表示される
sheet.detents = [.medium(), .large()]
}
present(vc, animated: true, completion: nil)
}
}
/// UISheetPresentationController に載せたい ViewController
final class ModalViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
}
}
sheet.detents
にセットする値を .medium()
だけにすると、
以下の gif のように上まで上がらず、真ん中までのモーダル画面にすることができます。
初期表示位置の変更
sheet.detents = [.medium(), large()]
と設定した場合、初期表示位置は .medium()
になります。
以下のように sheet.selectedDetentIdentifier
を指定する、
もしくは sheet.detents = [.large(), .medium()]
のように並び順を変更することで初期表示位置を変更することができます。
let vc = ModalViewController(nibName: nil, bundle: nil)
if let sheet = vc.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.selectedDetentIdentifier = UISheetPresentationController.Detent.Identifier.large
}
present(vc, animated: true, completion: nil)
高さの変更
高さの指定は UISheetPresentationController.detents
で指定可能ですが、
現状だと .medium()
と .large()
しか指定することができず、自由に高さを変更することはできないようです。
自由に高さを変更することはできませんが、以下のスクリーンショットのように iPad と iPhone で最適な UI になるように自動で調節してくれるようなので、この辺りはとても便利かなと思います。
.medium() | .large() |
---|---|
ハンドルの表示方法
ハーフモーダルを表示した際に画面の上部に灰色のバーが表示されていることを見かけると思います。
UISheetPresentationController
では prefersGrabberVisible
というプロパティを true
にすることで表示することができます。
(デフォルトは false
のようです)
sheet.prefersScrollingExpandsWhenScrolledToEdge = true // ハンドルを表示
prefersGrabberVisible = false (default) | prefersGrabberVisible = true |
---|---|
UIScrollView を載せた際の挙動
半モーダルに UIScrollView
や UITableView
、UICollectionView
を載せたいといった状況は多いかと思います。
そういった場合にどのような挙動をするのか、スムーズに動いてくれるのか確認してみたいと思います。
今回は UITableView を載せてみた際の挙動を確認してみたいと思います。
.medium() & .large() の場合 | .medium() のみの場合 |
---|---|
.medium()
→ .large()
への流れや .medium()
→ dismiss の流れなど、とてもスムーズに動いてくれました。
また、UIScrollView
の上部にタイトルのような View を追加したいという要件も多いと思いますが、
そういった場合でも問題なくスムーズに動作してくれました。
内部のコンテンツを上方向にスクロールした場合に .large()
のサイズまで拡張させたくない場合もあるかと思います。
そういった要件を満たすために prefersScrollingExpandsWhenScrolledToEdge
といったプロパティが用意されていました。
このプロパティを false
にすることで、内部のコンテンツをスクロールしても .large()
のサイズにはならず、
ユーザが意図的にハーフモーダルのサイズを変更した場合のみ .large()
のサイズへ拡張できるといった実装ができるようです。
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
モーダルアニメーションの速度の変更
モーダルの表示までのアニメーションやアニメーション速度を変更したい場合もあるかと思います。
現状 UISheetPresentationController
にはそういった変更ができそうなプロパティ等は定義されていなさそうなので、
今後のアップデートに期待しましょう。
終わりに
iOS15 以降からでしか使用できないかつ、まだまだ細かいところは調節ができないといったネックはありますが、
今までライブラリを導入して実装しないと面倒だったハーフモーダルの実装が簡単にできるところ、
iPad での表示の最適化、Landscape への対応なども踏まえると取り入れてみるのはアリなんじゃないかなと思いました。
今後より詳細な設定ができるようなアップデートに期待しています。