可点击的子字符串在SwiftUI中

huangapple go评论98阅读模式
英文:

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?

  1. @ViewBuilder
  2. func bodyTextView(text: String) -> some View {
  3. HStack {
  4. ForEach(attributedText(text: text), id: \.self) { text in
  5. if text.hasPrefix("@") {
  6. Text(text)
  7. .onTapGesture {
  8. print("TAP: \(text)")
  9. }
  10. } else {
  11. Text(text)
  12. }
  13. }
  14. }
  15. }
  16. func attributedText(text: String) -> [String] {
  17. let inputString = text
  18. let regexPattern = "@\\w+"
  19. guard let regex = try? NSRegularExpression(pattern: regexPattern) else {
  20. return [inputString]
  21. }
  22. let range = NSRange(inputString.startIndex..<inputString.endIndex, in: inputString)
  23. let matches = regex.matches(in: inputString, range: range)
  24. var substrings: [String] = []
  25. for match in matches {
  26. if let swiftRange = Range(match.range, in: inputString) {
  27. let substring = String(inputString[swiftRange])
  28. substrings.append(substring)
  29. }
  30. }
  31. return substrings
  32. }

答案1

得分: 1

以下是翻译好的部分:

你可以使用URLScheme创建一个URL,例如yourApp://users/username,然后使用Markdown + LocalizedStringKey和/或AttributedText,你的字符串应该类似于"[@sample](yourApp://users/sample)",然后你可以使用以下方式处理点击事件:

  1. @Environment(\.openURL) private var openURL

这进一步详细说明了一些内容。你可以使用以下方式替换当前的函数:

  1. extension String {
  2. /// 用`"@\\w+"`替换为`"[@\(username)](yourApp://users/\(username))"`
  3. func addAppURLToUser() -> String {
  4. var inputString = self
  5. let regexPattern = "@\\w+"
  6. guard let regex = try? NSRegularExpression(pattern: regexPattern) else {
  7. return inputString
  8. }
  9. let range = NSRange(inputString.startIndex..<inputString.endIndex, in: inputString)
  10. let matches = regex.matches(in: inputString, range: range)
  11. for match in matches.reversed() {
  12. if let swiftRange = Range(match.range, in: inputString) {
  13. var username = String(inputString[swiftRange])
  14. username.removeFirst()
  15. let url = &quot;[@\(username)](yourApp://users/\(username))&quot;
  16. inputString.replaceSubrange(swiftRange, with: url)
  17. }
  18. }
  19. return inputString
  20. }
  21. }

然后你可以像这样获取带有URL的文本:

  1. text.addAppURLToUser()

你可以在任何SwiftUI Text 中使用它:

  1. extension String {
  2. var key: LocalizedStringKey {
  3. LocalizedStringKey(self)
  4. }
  5. }

然后像这样调用它:Text(text.addAppURLToUser().key)

在支持iOS 15及更高版本的情况下,你可以在任何SwiftUI Text 中使用它,如下所示:

  1. @available(iOS 15, *)
  2. struct LinkView: View {
  3. let text = &quot;Tagging @sample @today and maybe someone else @test today I am attending with @someoneElse&quot;
  4. @Environment(\.openURL) private var openURL
  5. @State var selectedUser: String?
  6. var body: some View {
  7. VStack{
  8. Text(text.addAppURLToUser().key)
  9. .environment(\.openURL, OpenURLAction { url in
  10. if let components = URLComponents(url: url, resolvingAgainstBaseURL: true){
  11. var username = components.path
  12. username.removeFirst()
  13. selectedUser = username
  14. }
  15. return .handled
  16. })
  17. if let selectedUser = selectedUser {
  18. Text(&quot;tapped user = **\(selectedUser)**&quot;)
  19. }
  20. }
  21. }
  22. }

希望这对你有所帮助!

英文:

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 &quot;[@sample](yourApp://users/sample)&quot;

Then you can handle the tap with

  1. @Environment(\.openURL) private var openURL

This elaborates some more.

https://stackoverflow.com/questions/75192976/listen-to-link-click-in-swiftui-textview/75201168#75201168

You can replace your current function with something like

  1. extension String {
  2. /// Replaces `&quot;@\\w+&quot;` with `&quot;[@\(username)](yourApp://users/\(username))&quot;`
  3. func addAppURLToUser() -&gt; String {
  4. var inputString = self
  5. let regexPattern = &quot;@\\w+&quot;
  6. guard let regex = try? NSRegularExpression(pattern: regexPattern) else {
  7. return inputString
  8. }
  9. let range = NSRange(inputString.startIndex..&lt;inputString.endIndex, in: inputString)
  10. let matches = regex.matches(in: inputString, range: range)
  11. for match in matches.reversed() {
  12. if let swiftRange = Range(match.range, in: inputString) {
  13. var username = String(inputString[swiftRange])
  14. username.removeFirst()
  15. let url = &quot;[@\(username)](yourApp://users/\(username))&quot;
  16. inputString.replaceSubrange(swiftRange, with: url)
  17. }
  18. }
  19. return inputString
  20. }
  21. }

Then you can get URL filled text like this

  1. text.addAppURLToUser()

You can use it in any SwiftUI Text with

  1. extension String {
  2. var key: LocalizedStringKey {
  3. LocalizedStringKey(self)
  4. }
  5. }

Then call it like Text(text.addAppURLToUser().key)

  1. @available(iOS 15, *)
  2. struct LinkView: View {
  3. let text = &quot;Tagging @sample @today and maybe someone else @test today I am attending with @someoneElse&quot;
  4. @Environment(\.openURL) private var openURL
  5. @State var selectedUser: String?
  6. var body: some View {
  7. VStack{
  8. Text(text.addAppURLToUser().key)
  9. .environment(\.openURL, OpenURLAction { url in
  10. if let components = URLComponents(url: url, resolvingAgainstBaseURL: true){
  11. var username = components.path
  12. username.removeFirst()
  13. selectedUser = username
  14. }
  15. return .handled
  16. })
  17. if let selectedUser = selectedUser {
  18. Text(&quot;tapped user = **\(selectedUser)**&quot;)
  19. }
  20. }
  21. }
  22. }

huangapple
  • 本文由 发表于 2023年5月23日 00:02:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76308018.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定