7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

UISheetPresentationController を使ってみた

Posted at

はじめに

WWDC2021 で発表のあったハーフモーダルを実装することができる UISheetPresentationController を使ってみたので、
使用方法と使ってみた感想などを綴っておきたいと思います。

これまでは PanModalFloatingPanel 等のライブラリを使って実装していましたが、
公式でもサポートされたようなので使い勝手を調べてみました。

環境

Xcode: 13.0 beta 4 (13A5201i)
Swift: Swift5
iOS: iOS15(public beta 4)

使用方法

以下のようにするだけで簡単にモーダル画面を実装することができます。

ViewController
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)
    }
}
ModalViewController
/// UISheetPresentationController に載せたい ViewController
final class ModalViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
    }
}

上記のように記述するだけで簡単に使用することができます。
sample1.gif

sheet.detents にセットする値を .medium() だけにすると、
以下の gif のように上まで上がらず、真ん中までのモーダル画面にすることができます。

sample2.gif

初期表示位置の変更

sheet.detents = [.medium(), large()] と設定した場合、初期表示位置は .medium() になります。

以下のように sheet.selectedDetentIdentifier を指定する、
もしくは sheet.detents = [.large(), .medium()] のように並び順を変更することで初期表示位置を変更することができます。

ViewController
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()
スクリーンショット 2021-08-09 16.43.05.png スクリーンショット 2021-08-09 16.43.16.png

ハンドルの表示方法

ハーフモーダルを表示した際に画面の上部に灰色のバーが表示されていることを見かけると思います。
UISheetPresentationController では prefersGrabberVisible というプロパティを true にすることで表示することができます。
(デフォルトは false のようです)

sheet.prefersScrollingExpandsWhenScrolledToEdge = true // ハンドルを表示
prefersGrabberVisible = false (default) prefersGrabberVisible = true
Simulator Screen Shot - iPhone 12 - 2021-08-10 at 22.15.27.png Simulator Screen Shot - iPhone 12 - 2021-08-10 at 22.13.26.png

UIScrollView を載せた際の挙動

半モーダルに UIScrollViewUITableViewUICollectionView を載せたいといった状況は多いかと思います。
そういった場合にどのような挙動をするのか、スムーズに動いてくれるのか確認してみたいと思います。

今回は UITableView を載せてみた際の挙動を確認してみたいと思います。

.medium() & .large() の場合 .medium() のみの場合
sample4.gif sample5.gif

.medium().large() への流れや .medium() → dismiss の流れなど、とてもスムーズに動いてくれました。

また、UIScrollView の上部にタイトルのような View を追加したいという要件も多いと思いますが、
そういった場合でも問題なくスムーズに動作してくれました。

sample6.gif

内部のコンテンツを上方向にスクロールした場合に .large() のサイズまで拡張させたくない場合もあるかと思います。
そういった要件を満たすために prefersScrollingExpandsWhenScrolledToEdge といったプロパティが用意されていました。
このプロパティを false にすることで、内部のコンテンツをスクロールしても .large() のサイズにはならず、
ユーザが意図的にハーフモーダルのサイズを変更した場合のみ .large() のサイズへ拡張できるといった実装ができるようです。

sheet.prefersScrollingExpandsWhenScrolledToEdge = false

sample7.gif

モーダルアニメーションの速度の変更

モーダルの表示までのアニメーションやアニメーション速度を変更したい場合もあるかと思います。
現状 UISheetPresentationController にはそういった変更ができそうなプロパティ等は定義されていなさそうなので、
今後のアップデートに期待しましょう。

終わりに

iOS15 以降からでしか使用できないかつ、まだまだ細かいところは調節ができないといったネックはありますが、
今までライブラリを導入して実装しないと面倒だったハーフモーダルの実装が簡単にできるところ、
iPad での表示の最適化、Landscape への対応なども踏まえると取り入れてみるのはアリなんじゃないかなと思いました。

今後より詳細な設定ができるようなアップデートに期待しています。

7
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?