如何为UIImagePickerController添加UIBarButtonItem?
问题原因
UIImagePickerController的导航栏属于内部私有组件,直接在初始化实例后设置navigationItem.leftBarButtonItem会因为时机过早失效——系统会在后续生命周期流程中重置导航栏内容,覆盖你设置的按钮。
可行解决方案
方案1:延迟设置按钮(简单快速)
在present UIImagePickerController之后,利用异步队列延迟设置按钮,确保系统已完成导航栏初始化:
let imagePicker = UIImagePickerController() imagePicker.delegate = self // 配置picker的其他属性(如sourceType等) present(imagePicker, animated: true) { DispatchQueue.main.async { let sampleButton = UIBarButtonItem(title: "Example", style: .plain, target: self, action: #selector(self.sampleButtonHandle)) imagePicker.navigationItem.leftBarButtonItem = sampleButton } } // 按钮点击事件处理 @objc func sampleButtonHandle() { // 这里写你的逻辑,比如取消picker、跳转页面等 imagePicker.dismiss(animated: true) }
方案2:利用Method Swizzling修改生命周期方法(更可靠)
通过运行时交换UIImagePickerController的viewDidAppear方法,确保在导航栏完全加载后再设置按钮(注意:该方法需谨慎使用,遵循苹果审核规则):
- 创建UIImagePickerController的分类:
import UIKit extension UIImagePickerController { static func setupCustomBarButton() { let originalMethod = class_getInstanceMethod(self, #selector(viewDidAppear(_:))) let swizzledMethod = class_getInstanceMethod(self, #selector(customViewDidAppear(_:))) guard let orig = originalMethod, let swiz = swizzledMethod else { return } method_exchangeImplementations(orig, swiz) } @objc private func customViewDidAppear(_ animated: Bool) { // 调用原方法 self.customViewDidAppear(animated) // 创建自定义按钮 let sampleButton = UIBarButtonItem(title: "Example", style: .plain, target: self, action: #selector(customButtonTapped)) self.navigationItem.leftBarButtonItem = sampleButton } @objc private func customButtonTapped() { // 通过通知中心将点击事件传递给外部控制器 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ImagePickerCustomButtonTapped"), object: self) } }
- 在AppDelegate的启动方法中初始化交换:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UIImagePickerController.setupCustomBarButton() return true }
- 在你的业务控制器中监听通知:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(handleCustomButtonTap(_:)), name: NSNotification.Name(rawValue: "ImagePickerCustomButtonTapped"), object: nil) } @objc func handleCustomButtonTap(_ notification: Notification) { guard let picker = notification.object as? UIImagePickerController else { return } picker.dismiss(animated: true) // 添加你的业务逻辑 } // 记得在控制器销毁时移除通知 deinit { NotificationCenter.default.removeObserver(self) }
注意事项
- 苹果不建议子类化UIImagePickerController,因此尽量避免创建子类修改导航栏。
- 使用Method Swizzling时,需确保只交换一次,避免重复交换导致异常。
- 如果picker需要支持不同的sourceType(如相机、相册),测试时需覆盖所有场景,确保按钮正常显示。
内容的提问来源于stack exchange,提问作者Furkan Hanci
相关产品推荐
相关产品推荐

