SwiftUI 处理程序在降价文本视图中捕获点击的 link
SwiftUI handler to capture tapped link in markdown Text view
在 Swift 5.5 SwiftUI 中,现在可以在降价文本中包含 link。是否可以使用它在 Text
视图中为子文本添加点击处理程序?例如,我正在想象做类似下面的事情,但我还没有想出如何构建一个适用于此的 link (我每次都会收到 URL 错误)。设置某种自定义 url 模式是否可行?有没有更好的解决方案,比如我可以添加到 Text
视图中,其作用类似于 UITextView
的 shouldInteractWith
?目标是解决与提到的问题类似的问题 但不必回退到 UITextView 或非包装 HStacks 或带 ZStacks 的 GeometryReaders。
let baseText = "apple banana pear orange lemon"
let clickableText = baseText.split(separator: " ")
.map{ "[\([=11=])](domainThatImAllowedToCapture.../\([=11=]))" }
Text(.init(clickableText)).onOpenLink { link in
print(link.split(separator: "/").last) // prints "pear" if word pear is tapped.
}
您可以尝试类似此示例代码的操作。它遍历你的 baseText
,创建适当的 links,当 link 是 tapped/actioned 时,你可以添加更多代码来处理它。
struct ContentView: View {
let baseText = "apple banana pear orange lemon"
let baseUrl = "https://api.github.com/search/repositories?q="
var body: some View {
let clickableText = baseText.split(separator: " ").map{ "[\([=10=])](\(baseUrl)\([=10=]))" }
ForEach(clickableText, id: \.self) { txt in
let attributedString = try! AttributedString(markdown: txt)
Text(attributedString)
.environment(\.openURL, OpenURLAction { url in
print("---> link actioned: \(txt.split(separator: "=").last)" )
return .systemAction
})
}
}
}
使用@workingdog 描述的方法,我将其整理成以下工作解决方案:
import SwiftUI
struct ClickableText: View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
private var text: String
private var onClick: (_ : String) -> Void
init(text: String, _ onClick: @escaping (_ : String) -> Void) {
self.text = text
self.onClick = onClick
}
var body: some View {
Text(.init(toClickable(text)))
.foregroundColor(colorScheme == .dark ? .white : .black)
.accentColor(colorScheme == .dark ? .white : .black)
.environment(\.openURL, OpenURLAction { url in
let trimmed = url.path
.replacingOccurrences(of: "/", with: "")
.trimmingCharacters(in: .letters.inverted)
withAnimation {
onClick(trimmed)
}
return .discarded
})
}
private func toClickable(_ text: String) -> String {
// Needs to be a valid URL, but otherwise doesn't matter.
let baseUrl = "https://a.com/"
return text.split(separator: " ").map{ word in
var cleaned = String(word)
for keyword in ["(", ")", "[", "]"] {
cleaned = String(cleaned.replacingOccurrences(of: keyword, with: "\\(keyword)"))
}
return "[\(cleaned)](\(baseUrl)\(cleaned))"
}.joined(separator: " ")
}
}
在 Swift 5.5 SwiftUI 中,现在可以在降价文本中包含 link。是否可以使用它在 Text
视图中为子文本添加点击处理程序?例如,我正在想象做类似下面的事情,但我还没有想出如何构建一个适用于此的 link (我每次都会收到 URL 错误)。设置某种自定义 url 模式是否可行?有没有更好的解决方案,比如我可以添加到 Text
视图中,其作用类似于 UITextView
的 shouldInteractWith
?目标是解决与提到的问题类似的问题
let baseText = "apple banana pear orange lemon"
let clickableText = baseText.split(separator: " ")
.map{ "[\([=11=])](domainThatImAllowedToCapture.../\([=11=]))" }
Text(.init(clickableText)).onOpenLink { link in
print(link.split(separator: "/").last) // prints "pear" if word pear is tapped.
}
您可以尝试类似此示例代码的操作。它遍历你的 baseText
,创建适当的 links,当 link 是 tapped/actioned 时,你可以添加更多代码来处理它。
struct ContentView: View {
let baseText = "apple banana pear orange lemon"
let baseUrl = "https://api.github.com/search/repositories?q="
var body: some View {
let clickableText = baseText.split(separator: " ").map{ "[\([=10=])](\(baseUrl)\([=10=]))" }
ForEach(clickableText, id: \.self) { txt in
let attributedString = try! AttributedString(markdown: txt)
Text(attributedString)
.environment(\.openURL, OpenURLAction { url in
print("---> link actioned: \(txt.split(separator: "=").last)" )
return .systemAction
})
}
}
}
使用@workingdog 描述的方法,我将其整理成以下工作解决方案:
import SwiftUI
struct ClickableText: View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
private var text: String
private var onClick: (_ : String) -> Void
init(text: String, _ onClick: @escaping (_ : String) -> Void) {
self.text = text
self.onClick = onClick
}
var body: some View {
Text(.init(toClickable(text)))
.foregroundColor(colorScheme == .dark ? .white : .black)
.accentColor(colorScheme == .dark ? .white : .black)
.environment(\.openURL, OpenURLAction { url in
let trimmed = url.path
.replacingOccurrences(of: "/", with: "")
.trimmingCharacters(in: .letters.inverted)
withAnimation {
onClick(trimmed)
}
return .discarded
})
}
private func toClickable(_ text: String) -> String {
// Needs to be a valid URL, but otherwise doesn't matter.
let baseUrl = "https://a.com/"
return text.split(separator: " ").map{ word in
var cleaned = String(word)
for keyword in ["(", ")", "[", "]"] {
cleaned = String(cleaned.replacingOccurrences(of: keyword, with: "\\(keyword)"))
}
return "[\(cleaned)](\(baseUrl)\(cleaned))"
}.joined(separator: " ")
}
}