在 SwiftUI 中创建一个带有可点击文本的大段落

Kev*_*vin 9 ios swiftui

我收到了从云数据库中获取文本并将其转换为文本视图的任务

规则如下:每个以$开头并以$结尾的单词都需要加粗:

let str = "$random$"
let extractStr = "random"
Text(extractStr).bold()
Run Code Online (Sandbox Code Playgroud)

每个以 ~ 开头并以 ~ 结尾的单词都需要可点击

let str = "~random~"
let extractStr = "random"
Text(extractStr).onTapGesture { print("tapping")}
Run Code Online (Sandbox Code Playgroud)

每个以 % 开头并以 % 结尾的单词都需要是红色的

let str = "%random%"
let extractStr = "random"
Text(extractStr).foregroundColor(Color.red)
Run Code Online (Sandbox Code Playgroud)

等等。不同种类的规则。

总体目标是将所有文本合并为一个段落。

在没有任何手势的情况下对 Texts 视图求和时是可能的,但是当我尝试用点击手势对 Textview 求和时,一切都崩溃了

例如,我创建了一个长文本

let text1 = "$Hello$ my dear fellows. I want to provide #you# this
             %awesome% long Text. I shall $talk$ a bit more to show
             @you@ that when the text is $very very long$ , $it
             doesn’t behave as I wanted to$. \n\n Lets #investigate a
             bit more# and see how this text can behave. \n\n ~you can
             click me~ and ~you can click me also~ and if you need
             to, you have ~another clickable text~"
Run Code Online (Sandbox Code Playgroud)

我想要的结果是这样的:

在此处输入图片说明

所以这就是我尝试过的

我把它转换成一个数组:

let array1 = [$Hello$, my, dear, fellows., I, want, to, provide,
              #you#, this, %awesome, long, text. ......]
Run Code Online (Sandbox Code Playgroud)

之后,我做了一些检查并创建了一个文本数组:

var arrayOfText: [Text] = []
Run Code Online (Sandbox Code Playgroud)

我做了一些逻辑,最后得到了这个:

arrayOfText = [Text("Hello").bold(), Text("my"),
       Text("dear").underline(),
       Text("fellows."), Text("I"), Text("want"), Text("to")
       Text("provide"), Text("you").foregroundColor(.blue),
       Text("this"), Text("awesome").foregroundColor(.red),
       Text("long"), Text("text"), ... ... ...
       Text("another clickable text").onTapGesture{print("tap")}] // <-- Text with tap gesture, this is not valid...
Run Code Online (Sandbox Code Playgroud)

现在我可以遍历文本并总结它们:

var newText: Text = Text("")
for text in arrayOfText {
    newText = newText + text
}
Run Code Online (Sandbox Code Playgroud)

但它失败了......我无法将带有点击手势的文本分配到一个文本数组中

我收到此错误:

Could not cast value of type 'SwiftUI.ModifiedContent<SwiftUI.Text, SwiftUI.AddGestureModifier<SwiftUI._EndedGesture<SwiftUI.TapGesture>>>' (0x7fc8c08e9758) to 'SwiftUI.Text' (0x7fff8166cd30).
Run Code Online (Sandbox Code Playgroud)

所以我尝试做一些解决方法并将数组设置为 AnyView 的数组

var arrayOfText: [AnyView] = []
Run Code Online (Sandbox Code Playgroud)

但是当我将 Text 转换为 AnyView 时

我收到此错误:

Cast from 'AnyView' to unrelated type 'Text' always fails
Run Code Online (Sandbox Code Playgroud)

任何想法我怎样才能做到这一点?

更新

我尝试使用 Asperi 方法:

struct StringPresenter: View {
   let text1 = "hello $world$ I am %a% &new&\n\n~robot~"
   var body: some View {
       ForEach(text1.split(separator: "\n"), id: \.self) { line in
           HStack(spacing: 4) {
               ForEach(line.split(separator: " "), id: \.self) { part in
                  self.generateBlock(for: part)
               }
           }
       }
   }

   private func generateBlock(for str: Substring) -> some View {
       Group {
           if str.starts(with: "$") {
               Text(str.dropFirst().dropLast(1))
                   .bold()
           } else
           if str.starts(with: "&") {
               Text(str.dropFirst().dropLast(1))
                   .font(Font.body.smallCaps())
           } else
           if str.starts(with: "%") {
               Text(str.dropFirst().dropLast(1))
                   .italic().foregroundColor(.red)
           } else
           if str.starts(with: "~") {
               Text(str.dropFirst().dropLast(1))
                   .underline().foregroundColor(.blue).onTapGesture { print("tapping ")}
           }
           else {
               Text(str)
           }
       }
   }
Run Code Online (Sandbox Code Playgroud)

}

但它不适用于长文本。整个观点相互瓦解

这是它的样子:

在此处输入图片说明

Ale*_*huk 5

从 iOS 15 开始,您可以使用AttributedString或. 这样你就得到了多行格式的文本。MarkdownText

使用 Markdown 的示例:

Text("This text is **bold**, this is *italic*. This is a tappable link: [tap here](https://stackoverflow.com)")
Run Code Online (Sandbox Code Playgroud)

AttributedStringover的优点Markdown是它可以让您更好地控制格式。例如,您可以设置链接颜色或下划线颜色:

var string = AttributedString("")

// you can use markdown with AttributedString
let markdownText = try! AttributedString(markdown: "This is **bold** text. This is *italic* text.")

var tappableText = AttributedString(" I am tappable! ")
tappableText.link = URL(string: "https://stackoverflow.com")
tappableText.foregroundColor = .green
    
var underlinedText = AttributedString("This is underlined text.")
underlinedText.underlineStyle = Text.LineStyle(pattern: .solid, color: .red)
    
string.append(markdownText)
string.append(tappableText)
string.append(underlinedText)

Text(string)
Run Code Online (Sandbox Code Playgroud)

它看起来是这样的:

格式化文本

旁注:如果您希望可点击文本具有与在浏览器中打开 URL 不同的行为,您可以为您的应用程序定义自定义 URL 方案。然后,您将能够使用onOpenURL(perform:)注册处理程序来处理链接上的点击事件,以便在视图收到视图所在场景或窗口的 url 时调用。