Swift 4.0 iOS11.x中能否实现代理链?自定义TextField代理问题求助
自定义UITextField代理链失效问题求助
我在Swift 4.0 + iOS 11.x环境下,创建了一个继承UITextField并遵循UITextFieldDelegate的自定义文本字段类IDText。为了传递文本输入完成(退出编辑)的事件,我新增了ExitedFieldDelegate协议来实现代理链,但把视图控制器ConfigViewController设为该协议代理后并未生效。想请教这种实现方式是否可行,还是应该改用通知?
相关代码如下:
import UIKit protocol ExitedFieldDelegate { func exited(info: String) } class IDText: UITextField, UITextFieldDelegate { internal var zeus: ExitedFieldDelegate? = nil required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! delegate = self } required override init(frame: CGRect) { super.init(frame: frame) delegate = self } func textFieldDidBeginEditing(_ textField: UITextField) { self.textColor = UIColor.black } func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) { if (delegate != nil) { let info = self.text zeus?.exited(info: info!) } } }
视图控制器代码:
class ConfigViewController: UIViewController, ExitedFieldDelegate { func exited(info: String) { print("The brain has left the room") } // 实际设置:var blah = IDText(); blah.delegate = self }
问题分析与解决方案
这种代理链的实现方式完全可行,没必要直接切换到通知——你遇到的问题只是几个小的代码疏漏,修正后就能正常工作:
核心问题:自定义代理对象未赋值
你在ConfigViewController里只设置了blah.delegate = self,但这个delegate是UITextFieldDelegate类型的,而你的自定义事件是通过zeus这个ExitedFieldDelegate对象传递的。你完全没给zeus赋值,所以zeus?.exited(info: info!)这行代码因为zeus是nil,根本不会执行。次要问题:没必要的判断与强制解包
在textFieldDidEndEditing里的if (delegate != nil)判断毫无意义——因为你在init里已经把UITextField的delegate设为了self,这个条件永远为真。另外,self.text可能为nil,强制解包info!会有崩溃风险,建议做可选绑定或者默认值处理。
修正后的代码示例
1. 修正IDText类
import UIKit protocol ExitedFieldDelegate: AnyObject { // 添加AnyObject,限定为类类型代理,避免循环引用风险 func exited(info: String) } class IDText: UITextField, UITextFieldDelegate { weak var zeus: ExitedFieldDelegate? // 添加weak,避免循环引用 required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! delegate = self } required override init(frame: CGRect) { super.init(frame: frame) delegate = self } func textFieldDidBeginEditing(_ textField: UITextField) { self.textColor = .black } func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) { // 处理text为空的情况,避免强制解包 let inputInfo = text ?? "" zeus?.exited(info: inputInfo) } }
2. 修正ConfigViewController的设置代码
class ConfigViewController: UIViewController, ExitedFieldDelegate { func exited(info: String) { print("The brain has left the room - 输入内容:\(info)") } func setupTextField() { let blah = IDText(frame: CGRect(x: 20, y: 100, width: 300, height: 40)) // 不需要设置blah.delegate = self,因为IDText已经把自己设为UITextFieldDelegate了 // 如果你的VC需要处理UITextField的其他代理方法,再考虑调整逻辑 blah.zeus = self // 关键:给自定义代理对象赋值 view.addSubview(blah) } }
补充说明
- 给自定义协议添加
AnyObject,并把zeus声明为weak,是为了避免自定义文本字段和视图控制器之间的循环引用,防止内存泄漏。 - 代理方式相比通知的优势在于更直接、类型安全,能明确知道事件的发送者和接收者,适合这种一对一的事件传递场景;通知更适合一对多的全局事件通知。
内容的提问来源于stack exchange,提问作者user3069232
相关产品推荐
相关产品推荐

