Search code examples
swiftios17uidocumentpickerviewcontroller

How to use UIDocumentPickerViewController inside a ContainerView?


I am displaying a UIDocumentPickerViewController inside a containerView so user can pick document while being able to interact with other controls on the screen. It works and the documentPickerWasCancelled delegate does kick in when user taps cancel button. But when user selects a file and taps Open, the didPickDocumentsAt delegate method does not fire.

If I present the picker using present(pickerViewController, animated: true, completion: nil) then didPickDocumentsAt fires fine.

How can I make it work inside a Container?

class MainVC: UIViewController, UIDocumentPickerDelegate {
...
override func viewDidAppear(_ animated: Bool) {
    let pickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: [.item], asCopy: true)
    pickerViewController.delegate = self
    self.addChild(pickerViewController)
    self.containerView.addSubview(pickerViewController.view)
    pickerViewController.view.frame = self.containerView.bounds
    pickerViewController.didMove(toParent: self)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        print("cancelled") // <- Works
    }
    
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        print("picked") // <- Does not work when inside ContainerView
...
}

Solution

  • You can fix this by subclassing UIDocumentPickerViewController and overriding an internal _callDelegateWithSelectedURLsAndDismiss: method which works when the controller is presented modally only.

    class DocumentPickerViewController: UIDocumentPickerViewController {
      @objc
      func _callDelegateWithSelectedURLsAndDismiss(_ urls: [URL]) {
        delegate?.documentPicker?(self, didPickDocumentsAt: urls)
      }
    }
    

    Now the following code works for picking files:

    class ViewController: UIViewController {
      @IBOutlet private var containerView: UIView!
      
      override func viewDidAppear(_ animated: Bool) {
        let pickerViewController = DocumentPickerViewController(forOpeningContentTypes: [.item], asCopy: true)
        pickerViewController.delegate = self
        self.addChild(pickerViewController)
        self.containerView.addSubview(pickerViewController.view)
        pickerViewController.view.frame = self.containerView.bounds
      }
    }
    
    extension ViewController: UIDocumentPickerDelegate {
      func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        print(#function) // <- Works
      }
      
      func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        print(#function) // <- Works
      }
    }