扩展UIPickerViewDelegate协议:自定义可选方法未触发实现代码问题
哎,这个问题我之前也踩过坑!直接给UIPickerViewDelegate扩展加自定义可选方法的方式不可行,原因和Objective-C的runtime机制以及Swift协议扩展的派发规则有关,我给你拆解下:
为什么你的写法不起作用?
UIPickerViewDelegate是一个@objc协议,底层依赖Objective-C的动态消息转发机制。而Swift给@objc协议添加的扩展方法,并不会被Objective-C runtime识别——也就是说,当你通过pickerView.delegate调用这个自定义方法时,系统只会以Swift的静态派发方式调用扩展里的默认实现,完全不会去检查你的类是否实现了这个方法。
这就是为什么你明明写了自定义实现,却始终走默认逻辑的核心原因。
正确的实现方式
要实现“给UIPickerViewDelegate新增可选方法,支持默认实现+自定义重写”的需求,你需要自定义一个继承自UIPickerViewDelegate的新协议,借助@objc optional标记来让runtime识别这个方法,具体步骤如下:
1. 定义自定义协议
// 自定义协议,继承自UIPickerViewDelegate @objc protocol CustomPickerViewDelegate: UIPickerViewDelegate { // 标记为@objc optional,让OC runtime识别这个可选方法 @objc optional func doneClicked(_ pickerView: UIPickerView, selectedRow: Int, inComponent component: Int) } // 给自定义协议添加默认实现 extension CustomPickerViewDelegate { func doneClicked(_ pickerView: UIPickerView, selectedRow: Int, inComponent component: Int) { // 这里是默认实现逻辑 print("默认的done点击逻辑") } }
2. 修改UIPickerView的toolbar实现
在调用自定义方法时,需要将delegate强制转换为我们的自定义协议类型,这样才能触发正确的方法派发:
extension UIPickerView { func toolbar() -> UIToolbar? { let toolBar = UIToolbar() toolBar.barStyle = .default toolBar.isTranslucent = true // 创建Done按钮,绑定点击事件 let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneButtonTapped)) let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) toolBar.setItems([flexibleSpace, doneButton], animated: false) toolBar.sizeToFit() return toolBar } @objc private func doneButtonTapped() { let selectedRow = self.selectedRow(inComponent: 0) // 将delegate转为自定义协议,调用方法 // 这里如果实现了自定义方法,会优先调用;否则走默认实现 (delegate as? CustomPickerViewDelegate)?.doneClicked(self, selectedRow: selectedRow, inComponent: 0) } }
3. 在你的类中使用自定义协议
让你的视图控制器(或其他类)遵守CustomPickerViewDelegate协议,需要时重写doneClicked方法即可:
class YourViewController: UIViewController, CustomPickerViewDelegate { let pickerView = UIPickerView() override func viewDidLoad() { super.viewDidLoad() pickerView.delegate = self pickerView.addSubview(pickerView.toolbar()!) } // 重写自定义方法,替换默认逻辑 func doneClicked(_ pickerView: UIPickerView, selectedRow: Int, inComponent component: Int) { print("自定义的done点击逻辑,选中行:\(selectedRow)") } }
总结
直接给原生@objc协议扩展添加自定义可选方法的思路走不通,因为Objective-C runtime无法识别Swift协议扩展新增的方法。通过自定义继承协议+@objc optional标记的方式,既能保留默认实现,又能让系统正确识别并调用你的自定义重写逻辑。
内容的提问来源于stack exchange,提问作者u84six

