NSObject 不调用其他 类 的委托

NSObject not calling delegates of other classes

我有一个相当简单的 NSObject class,我称之为 MediaPicker。此 class 询问用户他们想要选择什么类型的媒体,然后根据他们的选择显示 UIImagePickerControllerUIDocumentPickerViewController

我面临的问题是 UIImagePickerControllerDelegateUIDocumentPickerViewControllerDelegate 方法没有被调用。

我的class如下:

import Foundation
import UIKit
import MobileCoreServices

public protocol MediaPickerDelegate: NSObjectProtocol {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
}

class MediaPicker: NSObject {
    //Data
    var viewController: UIViewController? = nil
    var imagePicker: UIImagePickerController = UIImagePickerController()
    let documentPicker = UIDocumentPickerViewController(documentTypes: [(kUTTypeImage as String), (kUTTypeMovie as String)], in: .import)

    //Delegate
    weak var delegate: MediaPickerDelegate? = nil

    override init() {
        super.init()
    }

    public func showMediaPicker(from presentingViewController: UIViewController) {
        //Set Data
        self.viewController = presentingViewController

        //Create Alert
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        //Add Library Action
        let libraryAction: UIAlertAction = UIAlertAction(title: "Photo & Video Library", style: .default) { (action) in
            self.presentLibraryPicker()
        }
        libraryAction.setValue(UIImage(named: "gallery_picker_library"), forKey: "image")
        libraryAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
        alertController.addAction(libraryAction)

        //Add Cloud Action
        let cloudAction: UIAlertAction = UIAlertAction(title: "Other Locations", style: .default) { (action) in
            self.presentDocumentPicker()
        }
        cloudAction.setValue(UIImage(named: "gallery_picker_cloud"), forKey: "image")
        cloudAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
        alertController.addAction(cloudAction)

        //Add Cancel Action
        let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)

        //Show Alert
        self.viewController?.present(alertController, animated: true, completion: nil)
    }

    private func presentLibraryPicker() {
        imagePicker.delegate = self
        imagePicker.sourceType = .photoLibrary
        imagePicker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .savedPhotosAlbum) ?? []
        imagePicker.allowsEditing = true
        self.viewController?.present(imagePicker, animated: true, completion: nil)
    }

    private func presentDocumentPicker() {
        documentPicker.delegate = self
        documentPicker.allowsMultipleSelection = false
        self.viewController?.present(documentPicker, animated: true, completion: nil)
    }
}

extension MediaPicker: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        delegate?.imagePickerController(picker, didFinishPickingMediaWithInfo: info)
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        delegate?.imagePickerControllerDidCancel(picker)
    }
}

extension MediaPicker: UIDocumentPickerDelegate {
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        delegate?.documentPickerWasCancelled(controller)
    }

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        delegate?.documentPicker(controller, didPickDocumentsAt: urls)
    }
}

extension MediaPicker: UINavigationControllerDelegate {
    //Redundant delegate implented as part of UIImagePickerControllerDelegate
}

调用这个的 class 看起来像这样:

class Foo: UIViewController {

    @objc func openPicker() {
        let mediaPicker: MediaPicker = MediaPicker()
        mediaPicker.delegate = self
        mediaPicker.showMediaPicker(from: self)
    }
}

我试过为 MediaPickerDelegate 制作 protocol classNSObjectProtocol,但没有成功。我也尝试过(考虑可能是我 obj-c 时代的 ARC 问题)使两个选择器变量在函数范围内成为全局变量和局部变量,但这似乎没有改变任何东西。

我也乐于接受您对我可能错过的 written/best 实践的代码质量的任何反馈。

是否尝试在 Info.plist 中获得照片库(隐私 - 照片库使用说明)的权限?

好的,所以我做了一些 testing/tweaking 并意识到两个 document/image 选择器控制器一显示就从内存中删除。这是因为它们在调用它们的函数范围内并且函数已完成执行。

我的意思是在函数 openPicker 中(来自问题)调用它的 class 正在做一些 ARC 魔法并在它认为函数是时将其删除调用完毕。因为 mediaPicker 对于 openPicker 是局部的而不是 class Foo 的全局它不明白它需要将它保存在内存中。

这里的解决方案是在 class Foo 内的 class 级别有一个全局变量,并在函数 openPicker 内访问它,而不是初始化 [=17 的新实例=]如下:

class Foo: UIViewController {
    //Data
    var mediaPicker: MediaPicker = MediaPicker()

    @objc func openPicker() {
        self.mediaPicker.delegate = self
        self.mediaPicker.showMediaPicker(from: self)
    }
}

一旦我迁移到使用这种方法,就会调用委托,并且调试器清楚地显示对象保留在内存中,而 class/UIViewController 仍然可以交互地用于用户。