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

自定义UITextView如何同时支持内部与外部两个delegate?

解决方案

方案1:重写delegate属性做中间转发(最常用,无侵入)

这个方案兼容UITextView原有使用习惯,外部开发者无感知,无需修改原有调用逻辑。
核心思路是:自定义控件内部始终持有自己作为真实delegate,单独存储外部设置的delegate,收到系统回调时先执行内部逻辑,再转发给外部delegate。

实现代码

import UIKit

class CustomTextView: UITextView {
    // 存储外部传入的delegate
    private weak var externalDelegate: UITextViewDelegate?
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        // 内部始终把自己设为真实的系统delegate
        super.delegate = self
    }
    
    // 重写delegate的get/set
    override weak var delegate: UITextViewDelegate? {
        get {
            return externalDelegate
        }
        set {
            externalDelegate = newValue
        }
    }
}

extension CustomTextView: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        // 第一步:执行内部私有逻辑
        print("CustomTextView delegate textViewDidChange (Performing operation like setNeedsDisplay to implement custom drawing)")
        // 第二步:转发回调给外部delegate(如果外部实现了该方法)
        externalDelegate?.textViewDidChange?(textView)
    }
    
    // 其他需要用到的UITextViewDelegate方法都按同样逻辑转发即可,例如:
    func textViewDidBeginEditing(_ textView: UITextView) {
        // 内部逻辑(如果有)
        externalDelegate?.textViewDidBeginEditing?(textView)
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        // 内部逻辑(如果有)
        externalDelegate?.textViewDidEndEditing?(textView)
    }
    
    // 其余代理方法按需添加转发即可
}

效果

外部ViewController无需修改任何代码,运行后会依次输出两行日志,符合预期。

方案2:多代理队列(适合需要支持多个外部代理的场景)

如果需要同时支持多个外部对象监听回调,可以用弱引用容器存储所有代理对象,收到系统回调时遍历调用。

实现代码

import UIKit

class CustomTextView: UITextView {
    // 用NSHashTable存储代理,weak持有避免循环引用
    private let delegates = NSHashTable<UITextViewDelegate>.weakObjects()
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        super.delegate = self
        // 把自己也加入代理队列,优先执行内部逻辑
        addDelegate(self)
    }
    
    // 暴露给外部添加代理的方法
    func addDelegate(_ delegate: UITextViewDelegate) {
        delegates.add(delegate)
    }
    
    // 也可以提供移除代理的方法
    func removeDelegate(_ delegate: UITextViewDelegate) {
        delegates.remove(delegate)
    }
}

extension CustomTextView: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        // 遍历所有代理执行回调
        delegates.allObjects.forEach {
            $0.textViewDidChange?(textView)
        }
    }
    
    // 其他代理方法同样遍历调用即可
}

外部使用示例

override func viewDidLoad() {
    super.viewDidLoad()
    customTextView.addDelegate(self)
}

这种方案的优势是可以支持任意数量的监听者,劣势是需要修改外部原有调用逻辑,不再兼容原生delegate赋值的写法。

内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 09:36:01