如何将UIImagePicker选中的图片动态传递至发起它的UIViewController
解决UIImagePicker选中图片传递及复用问题
你遇到的核心问题是在通用扩展里实现选中图片的回调解耦——既要把图片传给发起选择器的ViewController,又不能硬编码指定具体VC类型,保证扩展能在多个页面复用。下面给你两种iOS原生场景下最常用的解决方案:
方案一:闭包回调(推荐,轻量直观)
闭包是实现这种一次性回调最简洁的方式,我们可以给扩展添加一个可选闭包属性,在图片选择完成时触发它:
1. 修改ImageUploader.swift的扩展代码
import UIKit import Firebase extension UIViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { // 新增:定义可选闭包,用于回调选中的图片 var imagePickerCompletion: ((UIImage?) -> Void)? { get { return objc_getAssociatedObject(self, &AssociatedKeys.imagePickerCompletion) as? ((UIImage?) -> Void) } set { objc_setAssociatedObject(self, &AssociatedKeys.imagePickerCompletion, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } // 注意:Swift方法名首字母小写,符合命名规范 public func imagePicker() { let picker = UIImagePickerController() picker.delegate = self picker.allowsEditing = true present(picker, animated: true, completion: nil) } public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { var selectedImageFromPicker: UIImage? // 优先取编辑后的图片,无编辑图则取原图 if let editedImage = info[.editedImage] as? UIImage { selectedImageFromPicker = editedImage } else if let originalImage = info[.originalImage] as? UIImage { selectedImageFromPicker = originalImage } picker.dismiss(animated: true) { print("Image upload started") // 触发闭包,把图片传递回发起VC self.imagePickerCompletion?(selectedImageFromPicker) // 回调后清空闭包,避免潜在循环引用 self.imagePickerCompletion = nil } } // 处理用户取消选择的场景 public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) { self.imagePickerCompletion?(nil) self.imagePickerCompletion = nil } } } // 用于关联对象的Key,避免命名冲突 private struct AssociatedKeys { static var imagePickerCompletion = "imagePickerCompletion" }
2. 在createActivityVC中使用闭包接收图片
@IBAction func addPictureBtnWasPressed(_ sender: Any) { PHPhotoLibrary.requestAuthorization { status in DispatchQueue.main.async { // UI操作必须回到主线程 switch status { case .authorized: print("Access to photo authorized") // 设置回调闭包,接收选中的图片 self.imagePickerCompletion = { [weak self] selectedImage in guard let self = self, let image = selectedImage else { // 处理取消选择或图片为空的情况 return } // 这里拿到图片后,就可以在当前VC里处理(比如赋值给UIImageView、准备上传等) self.yourImageView.image = image } self.imagePicker() case .denied: print("Access to photo denied") case .restricted, .notDetermined: // 处理其他权限状态 break @unknown default: break } } } }
方案二:代理模式(适合复杂扩展场景)
如果你的图片选择逻辑后续需要扩展更多回调(比如取消选择、上传进度通知等),代理模式会更清晰易维护:
1. 定义代理协议
import UIKit protocol ImagePickerDelegate: AnyObject { func imagePickerDidSelectImage(_ image: UIImage?) func imagePickerDidCancel() }
2. 修改扩展代码
extension UIViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { // 新增代理属性,用weak避免循环引用 weak var imagePickerDelegate: ImagePickerDelegate? { get { return objc_getAssociatedObject(self, &AssociatedKeys.imagePickerDelegate) as? ImagePickerDelegate } set { objc_setAssociatedObject(self, &AssociatedKeys.imagePickerDelegate, newValue, .OBJC_ASSOCIATION_ASSIGN) } } public func imagePicker() { let picker = UIImagePickerController() picker.delegate = self picker.allowsEditing = true present(picker, animated: true, completion: nil) } public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { var selectedImageFromPicker: UIImage? if let editedImage = info[.editedImage] as? UIImage { selectedImageFromPicker = editedImage } else if let originalImage = info[.originalImage] as? UIImage { selectedImageFromPicker = originalImage } picker.dismiss(animated: true) { print("Image upload started") self.imagePickerDelegate?.imagePickerDidSelectImage(selectedImageFromPicker) } } public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) { self.imagePickerDelegate?.imagePickerDidCancel() } } } private struct AssociatedKeys { static var imagePickerDelegate = "imagePickerDelegate" }
3. 在createActivityVC中遵守代理协议
// 让VC遵守代理协议 class createActivityVC: UIViewController, ImagePickerDelegate { override func viewDidLoad() { super.viewDidLoad() // 设置代理 self.imagePickerDelegate = self } @IBAction func addPictureBtnWasPressed(_ sender: Any) { PHPhotoLibrary.requestAuthorization { status in DispatchQueue.main.async { switch status { case .authorized: print("Access to photo authorized") self.imagePicker() case .denied: print("Access to photo denied") default: break } } } } // 实现代理方法 func imagePickerDidSelectImage(_ image: UIImage?) { guard let image = image else { return } self.yourImageView.image = image // 后续业务逻辑处理 } func imagePickerDidCancel() { // 处理用户取消选择的情况 } }
关键注意点
- 使用
objc_getAssociatedObject给UIViewController添加关联属性,因为Swift扩展不能直接添加存储属性。 - 闭包回调时必须使用
[weak self]避免循环引用。 - 权限请求属于异步操作,后续的UI操作必须切回主线程执行,否则会引发崩溃。
- 两种方案都不需要绑定具体VC类型,所有UIViewController子类都能直接复用这个图片选择扩展。
内容的提问来源于stack exchange,提问作者Chris_1983_Norway
相关产品推荐
相关产品推荐

