NSObject 不调用其他 类 的委托
NSObject not calling delegates of other classes
我有一个相当简单的 NSObject class,我称之为 MediaPicker
。此 class 询问用户他们想要选择什么类型的媒体,然后根据他们的选择显示 UIImagePickerController
或 UIDocumentPickerViewController
。
我面临的问题是 UIImagePickerControllerDelegate
和 UIDocumentPickerViewControllerDelegate
方法没有被调用。
我的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
class
和 NSObjectProtocol
,但没有成功。我也尝试过(考虑可能是我 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
仍然可以交互地用于用户。
我有一个相当简单的 NSObject class,我称之为 MediaPicker
。此 class 询问用户他们想要选择什么类型的媒体,然后根据他们的选择显示 UIImagePickerController
或 UIDocumentPickerViewController
。
我面临的问题是 UIImagePickerControllerDelegate
和 UIDocumentPickerViewControllerDelegate
方法没有被调用。
我的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
class
和 NSObjectProtocol
,但没有成功。我也尝试过(考虑可能是我 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
仍然可以交互地用于用户。