英文:
Tappable Substrings In SwiftUI
问题
我在我的应用中有帖子,用户可以在帖子中标记其他用户,期望是这些标记可以被点击并在点击时触发一个函数。
举个例子,帖子:"hello @someUser and @otherUser",@someUser 和 @otherUser 应该可以被点击。
这是我尝试过的代码,但它只显示带有标记用户的文本,从上面的例子中只显示:"@someUser @otherUser",而不显示其他文本。点击实现确实有效,但没有显示其他文本,我觉得在这里使用 HStack 不是理想的。
在 SwiftUI 中有更好的处理方式吗?
英文:
I have posts on my app where users can tag other users, the expectation is that these tags should be tappable and trigger a function when tapped.
For example the post: "hello @someUser and @otherUser" the @someUser and @otherUser should be tappable.
This is the code I've tried, but it's only showing text with tagged users, from the example above it is only displaying: "@someUser @otherUser" without the rest of the text. The tapping implementation does work but no other text is displayed, and I don't think an HStack is ideal here
Is there a better way to handle this in SwiftUI?
@ViewBuilder
func bodyTextView(text: String) -> some View {
HStack {
ForEach(attributedText(text: text), id: \.self) { text in
if text.hasPrefix("@") {
Text(text)
.onTapGesture {
print("TAP: \(text)")
}
} else {
Text(text)
}
}
}
}
func attributedText(text: String) -> [String] {
let inputString = text
let regexPattern = "@\\w+"
guard let regex = try? NSRegularExpression(pattern: regexPattern) else {
return [inputString]
}
let range = NSRange(inputString.startIndex..<inputString.endIndex, in: inputString)
let matches = regex.matches(in: inputString, range: range)
var substrings: [String] = []
for match in matches {
if let swiftRange = Range(match.range, in: inputString) {
let substring = String(inputString[swiftRange])
substrings.append(substring)
}
}
return substrings
}
答案1
得分: 1
以下是翻译好的部分:
你可以使用URLScheme创建一个URL,例如yourApp://users/username
,然后使用Markdown + LocalizedStringKey
和/或AttributedText
,你的字符串应该类似于"[@sample](yourApp://users/sample)"
,然后你可以使用以下方式处理点击事件:
@Environment(\.openURL) private var openURL
这进一步详细说明了一些内容。你可以使用以下方式替换当前的函数:
extension String {
/// 用`"@\\w+"`替换为`"[@\(username)](yourApp://users/\(username))"`
func addAppURLToUser() -> String {
var inputString = self
let regexPattern = "@\\w+"
guard let regex = try? NSRegularExpression(pattern: regexPattern) else {
return inputString
}
let range = NSRange(inputString.startIndex..<inputString.endIndex, in: inputString)
let matches = regex.matches(in: inputString, range: range)
for match in matches.reversed() {
if let swiftRange = Range(match.range, in: inputString) {
var username = String(inputString[swiftRange])
username.removeFirst()
let url = "[@\(username)](yourApp://users/\(username))"
inputString.replaceSubrange(swiftRange, with: url)
}
}
return inputString
}
}
然后你可以像这样获取带有URL的文本:
text.addAppURLToUser()
你可以在任何SwiftUI Text
中使用它:
extension String {
var key: LocalizedStringKey {
LocalizedStringKey(self)
}
}
然后像这样调用它:Text(text.addAppURLToUser().key)
。
在支持iOS 15及更高版本的情况下,你可以在任何SwiftUI Text
中使用它,如下所示:
@available(iOS 15, *)
struct LinkView: View {
let text = "Tagging @sample @today and maybe someone else @test today I am attending with @someoneElse"
@Environment(\.openURL) private var openURL
@State var selectedUser: String?
var body: some View {
VStack{
Text(text.addAppURLToUser().key)
.environment(\.openURL, OpenURLAction { url in
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true){
var username = components.path
username.removeFirst()
selectedUser = username
}
return .handled
})
if let selectedUser = selectedUser {
Text("tapped user = **\(selectedUser)**")
}
}
}
}
希望这对你有所帮助!
英文:
You can create a URL using a URLScheme
Something like yourApp://users/username
and then use Markdown + LocalizedStringKey
and/or AttributedText
Your string should look like "[@sample](yourApp://users/sample)"
Then you can handle the tap with
@Environment(\.openURL) private var openURL
This elaborates some more.
You can replace your current function with something like
extension String {
/// Replaces `"@\\w+"` with `"[@\(username)](yourApp://users/\(username))"`
func addAppURLToUser() -> String {
var inputString = self
let regexPattern = "@\\w+"
guard let regex = try? NSRegularExpression(pattern: regexPattern) else {
return inputString
}
let range = NSRange(inputString.startIndex..<inputString.endIndex, in: inputString)
let matches = regex.matches(in: inputString, range: range)
for match in matches.reversed() {
if let swiftRange = Range(match.range, in: inputString) {
var username = String(inputString[swiftRange])
username.removeFirst()
let url = "[@\(username)](yourApp://users/\(username))"
inputString.replaceSubrange(swiftRange, with: url)
}
}
return inputString
}
}
Then you can get URL filled text like this
text.addAppURLToUser()
You can use it in any SwiftUI Text
with
extension String {
var key: LocalizedStringKey {
LocalizedStringKey(self)
}
}
Then call it like Text(text.addAppURLToUser().key)
@available(iOS 15, *)
struct LinkView: View {
let text = "Tagging @sample @today and maybe someone else @test today I am attending with @someoneElse"
@Environment(\.openURL) private var openURL
@State var selectedUser: String?
var body: some View {
VStack{
Text(text.addAppURLToUser().key)
.environment(\.openURL, OpenURLAction { url in
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true){
var username = components.path
username.removeFirst()
selectedUser = username
}
return .handled
})
if let selectedUser = selectedUser {
Text("tapped user = **\(selectedUser)**")
}
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论