iOS Swift:为UIViewController实现添加对象的协议方案咨询
最优方案:用协议扩展复用新增对象视图控制器的Done按钮逻辑
你的思路完全正确——用协议扩展来消除这类重复模板代码是非常合适的。我们只需要调整协议的定义方式,解决weak属性和Storyboard关联的问题,就能实现一次性编写、多VC复用的目标。
核心问题分析
你遇到的主要问题是Swift协议无法直接声明weak属性,但IBOutlet要求属性必须是weak的。不过这个问题很好解决:协议只需要约定属性的存在和类型,具体的weak修饰交给视图控制器的IBOutlet属性来处理即可。
完整实现方案
1. 定义协议与扩展
我们先完善AddingHandler协议,同时在扩展中实现通用的配置逻辑和默认的按钮点击行为:
import UIKit // 协议要求遵守者是UIViewController,确保能使用导航栏、dismiss等方法 protocol AddingHandler: UIViewController { var doneButton: UIBarButtonItem? { get set } func doneButtonPressed() } extension AddingHandler { // 通用的Done按钮配置方法 func configureDoneButton() { guard let doneButton = doneButton else { print("Warning: Done button not connected in Storyboard!") return } doneButton.isEnabled = false doneButton.target = self doneButton.action = #selector(doneButtonPressed) } // 默认的Done按钮点击行为,子类可重写 @objc func doneButtonPressed() { dismiss(animated: true, completion: nil) } // 通用的按钮状态更新方法,子类直接调用即可 func updateDoneButtonEnabled(_ enabled: Bool) { doneButton?.isEnabled = enabled } }
2. 在视图控制器中使用
现在任何需要新增对象的VC都可以快速接入这个逻辑,只需要3步:
- 遵守
AddingHandler协议 - 关联Storyboard中的Done按钮为
weakIBOutlet - 在
viewDidLoad中调用配置方法
class AddContactViewController: UIViewController, AddingHandler { // 用weak修饰IBOutlet,符合Storyboard关联要求 @IBOutlet weak var doneButton: UIBarButtonItem? // 示例:输入框监听,用来判断是否满足启用按钮的条件 @IBOutlet weak var nameTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() configureDoneButton() // 调用协议扩展的配置方法 // 监听输入变化,更新按钮状态 nameTextField.addTarget(self, action: #selector(checkInputValidity), for: .editingChanged) } // 自定义的Done按钮行为,重写默认实现 @objc func doneButtonPressed() { guard let name = nameTextField.text, !name.isEmpty else { return } // 执行新增联系人的业务逻辑... print("新增联系人:\(name)") // 调用默认的dismiss逻辑 super.doneButtonPressed() } // 检查输入有效性,更新按钮状态 @objc private func checkInputValidity() { let isInputValid = !(nameTextField.text?.isEmpty ?? true) updateDoneButtonEnabled(isInputValid) // 调用协议扩展的状态更新方法 } }
关键细节说明
- Weak属性处理:协议不强制属性的引用类型,VC中的IBOutlet用
weak修饰即可满足内存管理要求,协议只关心属性的存在性。 - 安全防护:
configureDoneButton中加入guard let检查,避免因Storyboard未关联按钮导致的崩溃,同时给出警告提示。 - 灵活性:默认的
doneButtonPressed实现提供了通用的dismiss逻辑,子类可按需重写;updateDoneButtonEnabled封装了按钮状态更新,子类无需直接操作按钮。 - 复用性:所有新增对象的VC(比如AddProductViewController、AddTaskViewController等)都可以直接套用这个模式,无需重复编写配置代码。
内容的提问来源于stack exchange,提问作者ajrlewis
相关产品推荐
相关产品推荐

