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を向上しましょうとの記載もありました。