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

如何为自定义UIView的UITextView子视图设置代理?

处理自定义View中子UITextView的文本修改事件

我太懂你这个场景了——自己封装了个带俩UITextView的自定义View(也就是你说的父视图A),现在要在ViewController里处理其中子视图C的文本修改逻辑对吧?这种情况核心要注意解耦,绝对不能让VC直接去扒自定义View里的子控件,不然以后你改自定义View的内部结构,VC的代码就得跟着大改,太坑了。下面给你几种iOS开发里常用的靠谱方案:

方案1:自定义View暴露代理接口(最符合iOS设计规范)

这跟系统控件的逻辑完全一致,扩展性最强,适合需要暴露多个事件的场景。

  • 第一步:给自定义View A定义专属的代理协议,把你需要的事件暴露出去:
protocol CustomViewADelegate: AnyObject {
    func customView(_ customView: CustomViewA, textViewCDidChange text: String)
    // 要是以后需要监听B的事件,直接加对应的方法就行
}
  • 第二步:在CustomViewA里添加代理属性,同时让自己成为C的UITextViewDelegate:
class CustomViewA: UIView {
    weak var delegate: CustomViewADelegate?
    
    private let textViewB = UITextView()
    private let textViewC = UITextView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupTextViews()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupTextViews()
    }
    
    private func setupTextViews() {
        // 这里省略B和C的布局、样式配置代码
        textViewC.delegate = self
    }
}

extension CustomViewA: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        // 确认是子视图C的话,把事件抛给代理
        guard textView === textViewC else { return }
        delegate?.customView(self, textViewCDidChange: textView.text ?? "")
    }
}
  • 第三步:在VC里遵守代理协议,实现对应的方法:
class ViewController: UIViewController, CustomViewADelegate {
    private let customViewA = CustomViewA()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        customViewA.delegate = self
        view.addSubview(customViewA)
        // 这里添加customViewA的布局代码
    }
    
    func customView(_ customView: CustomViewA, textViewCDidChange text: String) {
        // 在这里处理C文本变化的逻辑,比如输入校验、更新数据模型等
        print("TextView C的文本更新啦:\(text)")
    }
}

方案2:闭包回调(轻量简洁,适合少事件场景)

如果只需要监听一两个事件,闭包会比代理更清爽,代码也更紧凑。

  • 在CustomViewA里添加一个闭包属性,用来传递C的文本变化事件:
class CustomViewA: UIView {
    // 用可选类型,避免强制要求VC必须实现
    var textViewCChangeHandler: ((String) -> Void)?
    
    private let textViewB = UITextView()
    private let textViewC = UITextView()
    
    // 初始化和布局代码跟上面一致...
    
    private func setupTextViews() {
        textViewC.delegate = self
    }
}

extension CustomViewA: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        guard textView === textViewC else { return }
        textViewCChangeHandler?(textView.text ?? "")
    }
}
  • 然后在VC里直接设置闭包逻辑:
class ViewController: UIViewController {
    private let customViewA = CustomViewA()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 注意用[weak self]避免循环引用
        customViewA.textViewCChangeHandler = { [weak self] newText in
            self?.handleTextViewCUpdate(newText)
        }
        view.addSubview(customViewA)
    }
    
    private func handleTextViewCUpdate(_ text: String) {
        print("TextView C的新文本:\(text)")
        // 这里写你的业务逻辑
    }
}

方案3:通知中心(不推荐,特殊场景才用)

如果实在不想用代理或闭包,也可以用NotificationCenter,但这种方式耦合性很高,还容易出现通知泄漏,一般只在跨多层级传递事件时才考虑。

  • 在CustomViewA的textViewDidChange里发送通知:
extension CustomViewA: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        guard textView === textViewC else { return }
        NotificationCenter.default.post(
            name: NSNotification.Name("TextViewCContentChanged"),
            object: textView.text
        )
    }
}
  • 在VC里监听通知,记得在销毁时移除监听:
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleTextViewCChange(_:)),
            name: NSNotification.Name("TextViewCContentChanged"),
            object: nil
        )
    }
    
    @objc private func handleTextViewCChange(_ notification: Notification) {
        if let newText = notification.object as? String {
            print("TextView C更新:\(newText)")
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

最后说句心里话

优先选代理或者闭包,这俩都是低耦合的方案,完全符合面向对象的设计原则。以后你要是改CustomViewA的内部结构,比如把TextViewC换成别的控件,只要对外的接口不变,VC的代码根本不用动,维护起来超省心。

内容的提问来源于stack exchange,提问作者SrJaimito

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:51:25