2026/6/9 22:47:08
网站建设
项目流程
铜川商城网站建设,新媒体代运营公司,济南城市建设职业学院官网招生网,蚌埠网站制作公司哪家好一. 引言在实际开发中#xff0c;我们经常会遇到这样的需求#xff1a;点击一段英文文本中的某个单词弹出释义 / 高亮该单词或者执行自定义逻辑#xff08;查词、收藏等#xff09;比如下面这一段话 用户点击 finance#xff0c;而不是整句话。I have a passion for more…一. 引言在实际开发中我们经常会遇到这样的需求点击一段英文文本中的某个单词弹出释义 / 高亮该单词或者执行自定义逻辑查词、收藏等比如下面这一段话 用户点击 finance而不是整句话。I have a passion for more academic achievement in finance.在UIKit中这类需求并不陌生使用 UITextView配合 NSAttributedString通过 shouldInteractWith URL 精准拿到点击的 range但是问题来了。在 SwiftUI 中Text 并没有类似的回调那我们要如何精准识别用户点击的是哪一个单词二.SwiftUI 的难点在哪里SwiftUI 的 Text 组件有几个天然限制没有点击 range 的回调onTapGesture 只能监听整体没有内建的文本点击定位能力也就是说下面这种写法是不够的Text(text) .onTapGesture { // 无法知道点的是哪个词 }SwiftUI方便快捷那么就也注定会失去一些灵活。不过我们仍然有办法来实现这个功能。三. 实现方案实现该方案的核心思路就是把“点击文本” 转成 “URL事件”。解决方案的关键在于这句话SwiftUI 可以识别 AttributedString 中的 link并通过 openURL 回调暴露点击事件拆解开来的话大概分为4个步骤使用正则表达式拆分出每个英文单词给每个单词设置一个自定义 link点击时通过 openURL 拦截从 URL 中反推出被点击的单词接下来我们就开始实际操作一下。3.1 提取文本中英文单词这里我们使用正则而不是简单的 split( )原因是可以正确处理标点不受空格数量影响let pattern \\b[A-Za-z]\\b使用正则来提取每一个单词。3.2为每个单词设置可点击 link我们先定义一个 ClickableText 组件struct ClickableText: View { let text: String let onWordTap: (String) - Void }然后通过正在来生成AttributedStringprivate func makeAttributedText(from text: String) - AttributedString { var attributed AttributedString(text) let pattern \\b[A-Za-z]\\b let regex try? NSRegularExpression(pattern: pattern) let nsText text as NSString let range NSRange(location: 0, length: nsText.length) regex?.enumerateMatches(in: text, range: range) { match, _, _ in guard let match else { return } let word nsText.substring(with: match.range) if let range Range(match.range, in: attributed) { attributed[range].link URL(string: word://\(word)) } } return attributed }至此每个单词都是一个可以点的效果了。3.3拦截点击事件识别被点的单词接下来我们使用SwiftUI提供的openURL 环境值来统一处理链接点击。Text(attributed) .environment(\.openURL, OpenURLAction { url in guard url.scheme word, let word url.host else { return .systemAction } onWordTap(word) return .handled })至此我们就已经可以进准获取到用户点击的单词了。四. 效果优化虽然我们已经实现了点击效果但是呢现在有个问题文字我们并没有设置颜色却发现变成了蓝色而且就目前的情况来说我们不知道当前已经点击的是哪个单词。所以我们在上面的基础上再优化两个点恢复文字颜色。给点击单词添加高亮状态。4.1去掉默认的蓝色样式由于 link 默认会被当作系统链接处理文字会变成蓝色。但是解决方案非常简单Text(attributed) .tint(.primary)tint 控制的是 link 的显示颜色而不是文本本身。效果如下4.2给选中的单词添加高亮效果接下来我们引入一个选中状态Binding var selectedWord: String?当生成 AttributedString 时根据状态决定是否高亮if selectedWord word { attributed[range].backgroundColor Color.red }点击时更新状态onWordTap(word) selectedWord word最终完整代码如下struct ClickableText: View { let text: String let onWordTap: (String) - Void Binding var selectedWord: String? var body: some View { let attributed makeAttributedText(from: text) ZStack { Text(attributed) .tint(.primary) // ✅ 1. 去掉蓝色保持正文颜色 .environment(\.openURL, OpenURLAction { url in guard url.scheme word, let word url.host else { return .systemAction } onWordTap(word) selectedWord word return .handled }) } } private func makeAttributedText(from text: String) - AttributedString { var attributed AttributedString(text) let pattern \\b[A-Za-z]\\b guard let regex try? NSRegularExpression(pattern: pattern) else { return attributed } let nsText text as NSString let fullRange NSRange(location: 0, length: nsText.length) regex.enumerateMatches(in: text, range: fullRange) { match, _, _ in guard let match match else { return } let word nsText.substring(with: match.range) if let range Range(match.range, in: attributed) { attributed[range].link URL(string: word://\(word)) // ✅ 如果是当前选中的词加高亮 if selectedWord word { attributed[range].backgroundColor Color.red } } } return attributed } }五. 最终效果我们在使用它时只需要传递内容和选中内容。struct ContentView: View { State private var selectedWord: String? var body: some View { VStack { Text(selectedWord ?? ) .padding() .background(.thinMaterial) .cornerRadius(8) ClickableText(text: I have a passion for more academic achievement in finance., onWordTap: { word in selectedWord word print(Tapped word: \(word)) }, selectedWord: $selectedWord) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.yellow) } }效果如下六. 结语通过这篇文章我们完整地实现了一个SwiftUI 中可精准识别用户点击单词的 Text 组件。整个过程并没有依赖 UIKit也没有使用私有 API而是基于 SwiftUI 官方提供的能力逐步拆解并完成使用 AttributedString 描述可交互文本通过 link 将“文本点击”转化为事件借助 openURL 精准拦截并识别被点击的单词利用状态驱动实现选中高亮与样式控制核心思想只有一句话在 SwiftUI 中不再“计算用户点了哪里”而是提前把可点击的内容建模成数据再通过状态变化驱动 UI 更新。这也是 SwiftUI 与 UIKit 在设计理念上的根本区别。当然识别文本点击并不只有这一种实现方式实际项目中也可以根据复杂度选择不同方案有其它方案的伙伴也欢迎交流。