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

Swift中编程创建的自定义UITextView无法显示可点击链接

UITextView 可点击链接+点击切换编辑模式实现问题排查

你的代码存在4个核心问题,导致链接无法正常显示、交互不符合预期:

  • 视图未正确添加到层级:你定义了addAllSubviews()方法负责把textView加到父视图,但全流程没有调用过这个方法,textView根本不在渲染树中,自然无法正常显示内容。
  • 代理未设置:你实现了textViewDidEndEditing(_:)回调,但从未给textView设置delegate = self,编辑结束后切回非编辑态、重开链接检测的逻辑完全不会执行。
  • 链接检测必要属性缺失:UITextView的自动链接检测依赖isSelectable = true配置,非编辑状态下如果关闭选择能力,数据检测器不会把纯文本链接渲染为可点击的链接样式,你的代码没有显式开启该属性(如果自定义TextView默认把isSelectable设为false就会直接失效)。
  • 自定义点击手势冲突:你添加的全局tap手势优先级高于UITextView内置的链接点击手势,会直接拦截所有点击事件,系统既没机会识别链接点击,也无法正常完成链接的渲染初始化。同时手势没有做区域判断,哪怕后续修复冲突,用户点链接也会直接触发编辑模式,不符合交互预期。

修正方案

按以下步骤补全配置即可:

  1. 补全视图挂载逻辑,在layoutTextView()方法末尾调用addAllSubviews(),确保textView被正确加入父视图。
  2. 给textView设置代理,在布局方法中添加textView?.delegate = self,让编辑生命周期回调正常触发。
  3. 显式开启必要属性:非编辑状态下保持textView?.isSelectable = true,不要关闭文本选择能力,否则链接检测不生效。
  4. 解决手势冲突:给tap手势设置代理,在手势触发前判断点击位置,如果点击区域落在链接上就不触发自定义编辑手势,交给系统处理链接交互。
  5. 优化编辑态切换逻辑:退出编辑时主动刷新textView的链接渲染,避免状态切换后链接样式丢失。

关键修正代码

class YourViewController: UIViewController, UITextViewDelegate, UIGestureRecognizerDelegate {
    var textView: TextView?

    override public func viewDidLoad() {
        super.viewDidLoad()
        textView = TextView(
            editorConfig: EditorConfig(theme: getTheme())
        )
    }

    override public func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        view.backgroundColor = .white
        layoutTextView()
    }

    private func layoutTextView() {
        textView?.frame = CGRect(
            x: view.safeAreaInsets.left,
            y: view.safeAreaInsets.top,
            width: view.bounds.width - view.safeAreaInsets.left - view.safeAreaInsets.right,
            height: view.frame.size.height / 3
        )

        textView?.layer.borderWidth = 1
        textView?.layer.borderColor = UIColor.gray.cgColor
        // 补全必要配置
        textView?.delegate = self
        textView?.isEditable = false
        textView?.isSelectable = true // 必须开启,否则链接不渲染
        textView?.isUserInteractionEnabled = true
        textView?.textColor = .black
        textView?.dataDetectorTypes = .link
        textView?.text = "https://www.google.com"
        
        let tap = UITapGestureRecognizer(target: self, action: #selector(textViewTapped))
        tap.delegate = self
        textView?.addGestureRecognizer(tap)

        // 把textView加到视图上
        addAllSubviews()
    }

    @objc func textViewTapped(_ aRecognizer: UITapGestureRecognizer) {
        textView?.isEditable = true
        textView?.becomeFirstResponder()
    }
    
    // 编辑结束回调正常触发
    func textViewDidEndEditing(_ textView: UITextView) {
        textView.isEditable = false
        textView.isSelectable = true
        textView.dataDetectorTypes = .link
        textView.layoutIfNeeded()
    }

    // 手势代理:点到链接时不触发编辑手势
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard let tv = textView else { return true }
        let point = gestureRecognizer.location(in: tv)
        let charIndex = tv.layoutManager.characterIndex(for: point, in: tv.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        guard charIndex < tv.textStorage.length else { return true }
        // 点击位置是链接的话,拦截自定义手势
        if tv.textStorage.attribute(.link, at: charIndex, effectiveRange: nil) != nil {
            return false
        }
        return true
    }

    private func addAllSubviews() {
        if let textView = textView {
            view.addSubview(textView)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:54:17