You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:31:17