【Swift】PHPickerViewControllerをスワイプダウンで閉じないようにする方法

2022.12.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

PHPickerViewControllerをスワイプダウンで閉じないようにする方法を調べたので記事にしておきます。

環境

  • Xcode 14.1
  • iOS 16.1

iOSデバイスでPHPickerViewController.pageSheet.formSheetのようなpresentationStyleで表示し、スワイプダウンで閉じさせたくないという対応をしたい時に、通常のViewControllerであればisModalInPresentationtrueにすれば閉じないように出来るのに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の変更が反映されるので、PHPickerViewControllerでラップして、そのラップしているViewControllerisModalInPresentationを変更します。

/// スワイプダウンで閉じれない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を向上しましょうとの記載もありました。

参考