You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:50:34