如何在UITextView中添加可点击超链接触发自定义函数实现类维基弹窗
UITextView 应用内自定义可点击链接实现
核心逻辑
不用让自定义URL走系统跳转流程,所有点击拦截都可以在UITextView的代理回调里完成。自定义URL本质上只是一个携带点击标识的传输载体,不需要真的被系统解析打开。
实现步骤
1. 设计自定义URL规则
自己定一套方便解析的URL格式就行,完全不需要和系统做关联,比如统一用wiki://term/{词条ID}的结构:
- scheme字段可以随便定,比如示例里用的
wiki - host字段存业务需要的参数,比如词条唯一ID、对应功能的路由值
举个例子:要给“增强现实”这个词汇绑定词条数据,就把链接属性的值设为wiki://term/arkit
这类纯内部拦截用的自定义URL,完全不需要在Info.plist里注册URL Scheme,因为根本不会把URL交给系统处理。
2. UITextView基础配置
先把基础属性配好,不然链接根本点不动:
- 关闭编辑能力:
isEditable = false - 开启文本可选能力:
isSelectable = true - 绑定代理对象:
delegate = self
3. 构造带链接的富文本
给需要可点击的文字范围挂上.link属性,属性值就是你设计好的自定义URL。
4. 拦截点击事件,触发自定义交互
在UITextView的代理方法里匹配自定义的URL scheme,解析出携带的参数,调用你自己实现的维基样式弹窗函数,最后返回false阻断系统默认的打开行为。
下面是完整可运行的示例代码:
class ViewController: UIViewController, UITextViewDelegate { @IBOutlet weak var textView: UITextView! override func viewDidLoad() { super.viewDidLoad() // 必配置项,否则链接无响应 textView.isEditable = false textView.delegate = self // 构造富文本内容 let fullText = "ARKit 是苹果推出的增强现实开发框架,其中屏幕空间环境光遮蔽可以显著提升渲染真实感。" let attributedString = NSMutableAttributedString(string: fullText) // 给指定词汇绑定自定义链接 let linkRange = (fullText as NSString).range(of: "屏幕空间环境光遮蔽") let linkURL = "wiki://term/ssao" attributedString.addAttribute(.link, value: linkURL, range: linkRange) textView.attributedText = attributedString } // 拦截文本交互事件 func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { // 匹配自定义的内部链接scheme if URL.scheme == "wiki" { // 解析出链接携带的词条ID guard let termID = URL.host else { return false } // 调用自定义弹窗逻辑 showWikiPopover(termID: termID, sourceRange: characterRange) // 返回false阻断系统默认打开行为 return false } // 其他类型的外部链接(如http/https)可以保留系统默认打开逻辑 return true } // 维基样式小型弹窗实现 private func showWikiPopover(termID: String, sourceRange: NSRange) { // 这里根据termID获取对应的词条说明内容,可对接本地存储或网络请求 let detailText = "屏幕空间环境光遮蔽(SSAO)是一种在屏幕空间计算场景阴影的渲染技术,能以较低性能开销提升画面真实感。" let popVC = UIViewController() popVC.modalPresentationStyle = .popover popVC.preferredContentSize = CGSize(width: 280, height: 180) // 给弹窗添加内容标签 let contentLabel = UILabel(frame: CGRect(x: 12, y: 12, width: 256, height: 156)) contentLabel.numberOfLines = 0 contentLabel.text = detailText popVC.view.addSubview(contentLabel) // 把弹窗的箭头锚定到被点击的文字位置 if let popPC = popVC.popoverPresentationController, let textPosition = textView.position(from: textView.beginningOfDocument, offset: sourceRange.location) { let anchorRect = textView.caretRect(for: textPosition) popPC.sourceView = textView popPC.sourceRect = anchorRect popPC.permittedArrowDirections = [.up, .down] } present(popVC, animated: true) } }
常见踩坑提示
- 默认的链接样式是蓝色带下划线,可以通过
textView.linkTextAttributes统一调整,比如设置成App主题色、移除下划线,和整体UI保持一致 - 弹窗锚点不需要手动硬编码坐标,通过UITextView的文本定位API可以精准拿到被点击文字的位置,对齐效果和维基百科的交互完全一致
- 自定义链接和普通外部链接的逻辑互不干扰:代理方法里对不同scheme做分支判断即可,外部链接可以正常走系统打开流程
内容的提问来源于stack exchange,提问作者Eric33187
相关产品推荐
相关产品推荐

