如何使用带代理的UIImagePickerController?子视图类获取图片返回问题
解决UIView子类获取ViewController图片选择结果的方案
我来帮你搞定这个问题!你之前用代理出各种错误,大概率是协议定义、代理属性设置或者代理赋值环节踩坑了——比如没给代理加weak导致循环引用、忘记设置aView.delegate = self,或者协议没声明为类专属。下面给你两种靠谱的实现方式,按需选择:
方案一:正确实现代理模式
这种方式适合需要多个回调的场景,扩展性更强。
第一步:在A类中定义代理协议
import UIKit // 定义类专属协议,保证代理可以用weak修饰 protocol AViewDelegate: AnyObject { // 通知ViewController需要触发图片选择器 func aViewDidRequestImage(_ view: AView) } class AView: UIView { // 用weak修饰代理,避免循环引用 weak var delegate: AViewDelegate? // 假设这是你触发选图的按钮点击事件 @IBAction func chooseImageBtnTapped(_ sender: UIButton) { // 通知代理发起图片选择请求 delegate?.aViewDidRequestImage(self) } // 供ViewController调用,用来接收并显示图片 func updateImageView(with image: UIImage?) { // 这里把图片赋值给A类里的UIImageView // self.imageView.image = image } }
第二步:在ViewController中实现代理
import UIKit class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, AViewDelegate { var aView: AView! override func viewDidLoad() { super.viewDidLoad() // 从xib加载A类实例并添加到视图 let nib = UINib(nibName: "AView", bundle: nil) aView = nib.instantiate(withOwner: nil, options: nil).first as! AView aView.frame = CGRect(x: 50, y: 100, width: 300, height: 300) // 关键!给AView设置代理,否则代理方法不会执行 aView.delegate = self view.addSubview(aView) } // MARK: - AViewDelegate 实现 func aViewDidRequestImage(_ view: AView) { // 弹出图片选择器 let imagePicker = UIImagePickerController() imagePicker.delegate = self imagePicker.sourceType = .photoLibrary // 可以改成.camera present(imagePicker, animated: true) } // MARK: - UIImagePickerControllerDelegate 实现 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { picker.dismiss(animated: true) // 获取选中的原图 let selectedImage = info[.originalImage] as? UIImage // 直接调用A类的方法把图片传回去 aView.updateImageView(with: selectedImage) } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } }
代理模式避坑指南
- 协议必须继承
AnyObject(或者加@objc),这样代理属性才能用weak修饰,避免循环引用导致内存泄漏和回调无响应。 - 一定要记得设置
aView.delegate = self,这是最容易忘的点,没设置的话代理方法根本不会触发。 - 代理方法调用时加
?,避免代理为nil时崩溃。
方案二:更简洁的闭包回调
如果只是单一的选图回调,闭包写法会更简洁,不用写协议。
第一步:在A类中定义闭包属性
import UIKit class AView: UIView { // 定义闭包,用来通知ViewController触发选图 var onRequestImage: (() -> Void)? // 供ViewController调用的图片更新方法 func updateImageView(with image: UIImage?) { // 处理图片显示逻辑 // self.imageView.image = image } @IBAction func chooseImageBtnTapped(_ sender: UIButton) { // 调用闭包通知外部 onRequestImage?() } }
第二步:在ViewController中绑定闭包
import UIKit class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { var aView: AView! override func viewDidLoad() { super.viewDidLoad() let nib = UINib(nibName: "AView", bundle: nil) aView = nib.instantiate(withOwner: nil, options: nil).first as! AView aView.frame = CGRect(x: 50, y: 100, width: 300, height: 300) // 绑定闭包,注意用[weak self]避免循环引用 aView.onRequestImage = { [weak self] in guard let self = self else { return } let imagePicker = UIImagePickerController() imagePicker.delegate = self imagePicker.sourceType = .photoLibrary self.present(imagePicker, animated: true) } view.addSubview(aView) } // MARK: - UIImagePickerControllerDelegate func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { picker.dismiss(animated: true) let selectedImage = info[.originalImage] as? UIImage aView.updateImageView(with: selectedImage) } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } }
闭包模式注意点
- 闭包中一定要用
[weak self],否则AView持有闭包,闭包持有ViewController,会造成循环引用,内存无法释放。 - 调用闭包时加
?,防止闭包未赋值导致崩溃。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

