【Swift】PHPickerViewControllerをスワイプダウンで閉じないようにする方法
PHPickerViewControllerをスワイプダウンで閉じないようにする方法を調べたので記事にしておきます。
環境
- Xcode 14.1
- iOS 16.1
iOSデバイスでPHPickerViewController
を.pageSheet
や.formSheet
のようなpresentationStyleで表示し、スワイプダウンで閉じさせたくないという対応をしたい時に、通常のViewController
であればisModalInPresentation
をtrue
にすれば閉じないように出来るのにPHPickerViewController
では機能しませんでした。
試したコード
import UIKit import PhotosUI class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) presentPhotoPicker() } private func presentPhotoPicker() { var configuration = PHPickerConfiguration() configuration.filter = .images configuration.selectionLimit = 1 let picker = PHPickerViewController(configuration: configuration) picker.delegate = self // スワイプダウンで閉じるを出来ないようにする picker.isModalInPresentation = true present(picker, animated: true) } } // MARK: - PHPickerViewController Delegate extension ViewController: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // do something dismiss(animated: true) } }
picker.isModalInPresentation = true
にしているはずなのにしっかり閉じれてしまいます。
PHPickerをViewControllerでラップする
通常のViewController
だとisModalInPresentation
の変更が反映されるので、PHPicker
をViewController
でラップして、そのラップしているViewController
のisModalInPresentation
を変更します。
/// スワイプダウンで閉じれないPHPickerViewController private class SwipeDownDisablePHPickerViewController: UIViewController { init(picker: PHPickerViewController) { super.init(nibName: nil, bundle: nil) // スワイプダウンで閉じるを出来ないようにする isModalInPresentation = true addChild(picker) picker.didMove(toParent: self) view.addSubview(picker.view) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
あとは、呼び出し時にスワイプダウンで閉じれないPHPickerViewControllerを生成して、表示してあげるだけです。
import UIKit import PhotosUI class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) presentPhotoPicker() } private func presentPhotoPicker() { var configuration = PHPickerConfiguration() configuration.filter = .images configuration.selectionLimit = 1 let picker = PHPickerViewController(configuration: configuration) picker.delegate = self let SwipeDownDisablePicker = SwipeDownDisablePHPickerViewController(picker: picker) present(SwipeDownDisablePicker, animated: true) } } // MARK: - PHPickerViewController Delegate extension ViewController: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // do something dismiss(animated: true) } }
isModalInPresentation = true
が機能してスワイプダウンで閉じれなくなりました。
おわりに
UIImagePickerController
でも同じことが発生するようです。ただかなり特殊な方法なので、PHPickerViewController
を.fullScreen
で表示するや、スワイプダウンで閉じれてもいいように検討するなどしても良いかもしれません。
今回はPHPickerViewController
だったのでキャンセルボタンを押下するとfunc picker(_ picker:, didFinishPicking)
のデリゲートメソッドが呼ばれる為、そこで処理を書けばよいですが、通常のViewControllerの場合に
Apple公式 Disabling the Pull-Down Gesture for a Sheetでは、ユーザーにプレゼンテーションを却下できない理由を説明してUXを向上しましょうとの記載もありました。