Search code examples
swiftuikittableview

Perform UIDocumentPickerView in a TableViewCell


I'm trying to open a picker file menu. To perform this, I create a class like so:

class pickerView: UIDocumentPickerViewController, UIDocumentPickerDelegate, LTHM_Pickerable {

func importTapped() {
    //Create a picker specifying file type and mode
    let documentPicker = UIDocumentPickerViewController(documentTypes: [String(kUTTypePNG)], in: .import)
    documentPicker.delegate = self
    documentPicker.allowsMultipleSelection = false
    documentPicker.modalPresentationStyle = .fullScreen
    present(documentPicker, animated: true, completion: nil)
}

public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    guard controller.documentPickerMode == .import, let url = urls.first, let image = UIImage(contentsOfFile: url.path) else { return }
    
    controller.dismiss(animated: true)
}

public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
    controller.dismiss(animated: true)
}

}

I want to call the function "importTapped" in a function with is in a class inheriting from UITableViewCell.

class LTHM_Sticker_Cell: UITableViewCell {
     @IBAction func createStickerOnTAp(_ sender: UIButton, forEvent event: UIEvent) {
         //CALL IMPORTTAPPED HERE
     }
}

can you please help me? I have tried with a protocol, but I'm not sure...


Solution

  • Having a UIDocumentPickerViewController subclass that creates an instance of the regular UIDocumentPickerViewController is a bit confusing, and it's missing a clear means to actually present it. You might want to try this protocol/delegate solution instead:

    Define a protocol

    protocol PickerPresenter: UIViewController {
        func presentPicker()
    }
    

    Create the view controller class that will contain your table view

    class MyViewController: UIViewController  {
       // your implementation
    }
    

    Conform your view controller to the protocol

    extension MyViewController: PickerPresenter {
        
        func presentPicker() {
            let documentPicker = UIDocumentPickerViewController(documentTypes: [String(kUTTypePNG)], in: .import)
            documentPicker.delegate = self
            documentPicker.allowsMultipleSelection = false
            documentPicker.modalPresentationStyle = .fullScreen
            present(documentPicker, animated: true, completion: nil)
        }
    }
    

    And to be the picker delegate

    extension MyViewController: UIDocumentPickerDelegate {
    
        public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            guard controller.documentPickerMode == .import, let url = urls.first, let image = UIImage(contentsOfFile: url.path) else { return }
            // do something with the selected image
            controller.dismiss(animated: true)
        }
    
        public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
            controller.dismiss(animated: true)
        }
    }
    

    Pass a reference to the view controller when you're dequeuing the cell

    extension MyViewController: UITableViewDataSource {
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! LTHM_Sticker_Cell
            cell.delegate = self
            return cell
        }
    }
    

    Finally, your cell class has a weak reference to the delegate, and calls it's delegate method when the button is tapped

    class LTHM_Sticker_Cell: UITableViewCell {
        
        weak var delegate: PickerPresenter?
        
        @IBAction func createStickerOnTAp(_ sender: UIButton, forEvent event: UIEvent) {
            delegate?.presentPicker()
        }
    }