Swift中编程创建的自定义UITextView无法显示可点击链接
UITextView 可点击链接+点击切换编辑模式实现问题排查
你的代码存在4个核心问题,导致链接无法正常显示、交互不符合预期:
- 视图未正确添加到层级:你定义了
addAllSubviews()方法负责把textView加到父视图,但全流程没有调用过这个方法,textView根本不在渲染树中,自然无法正常显示内容。 - 代理未设置:你实现了
textViewDidEndEditing(_:)回调,但从未给textView设置delegate = self,编辑结束后切回非编辑态、重开链接检测的逻辑完全不会执行。 - 链接检测必要属性缺失:UITextView的自动链接检测依赖
isSelectable = true配置,非编辑状态下如果关闭选择能力,数据检测器不会把纯文本链接渲染为可点击的链接样式,你的代码没有显式开启该属性(如果自定义TextView默认把isSelectable设为false就会直接失效)。 - 自定义点击手势冲突:你添加的全局tap手势优先级高于UITextView内置的链接点击手势,会直接拦截所有点击事件,系统既没机会识别链接点击,也无法正常完成链接的渲染初始化。同时手势没有做区域判断,哪怕后续修复冲突,用户点链接也会直接触发编辑模式,不符合交互预期。
修正方案
按以下步骤补全配置即可:
- 补全视图挂载逻辑,在
layoutTextView()方法末尾调用addAllSubviews(),确保textView被正确加入父视图。 - 给textView设置代理,在布局方法中添加
textView?.delegate = self,让编辑生命周期回调正常触发。 - 显式开启必要属性:非编辑状态下保持
textView?.isSelectable = true,不要关闭文本选择能力,否则链接检测不生效。 - 解决手势冲突:给tap手势设置代理,在手势触发前判断点击位置,如果点击区域落在链接上就不触发自定义编辑手势,交给系统处理链接交互。
- 优化编辑态切换逻辑:退出编辑时主动刷新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
相关产品推荐
相关产品推荐

