在SwiftUI中,是否可以将Button<Text>添加到Text中?

7

通常添加两个文本视图将产生一个文本视图,保留每个单独的文本视图的属性。例如:

Text(“Hello ”) + Text(“world!”).foregroundColor(.blue)

这段代码应该会产生Hello world!,其中world!是蓝色的。现在,如果我想让world!可点击并像按钮一样工作,我该怎么做呢?逻辑上讲,应该这样做:
Text(“Hello ”) + Button(action: {}) { Text(“world!”).foregroundColor(.blue) }

然而,上面的代码不能直接编译。是否可以像描述的那样创建具有可点击部分的文本视图?我的使用情况是针对带有@提到或#主题标记的推文,在其中您将点击提到或主题标记以显示详细视图。
编辑:之所以在HStack中使用Text view和Button不合适,是因为这在返回推文内容的Text view的函数中。
func styledText(text: String) -> Text {
    var output = Text("")
    let components = text.tokenize("@#. ")
    for component in components {
        if component.rangeOfCharacter(from: CharacterSet(charactersIn: "@#")) != nil {
            output = output + Text(component).foregroundColor(.accentColor)
        } else {
            output = output + Text(component)
        }
    }
    return output
}

String.tokenize 是一个如下所示的函数:

extension String {
    func tokenize(_ delimiters: String) -> [String] {
        var output = [String]()
        var buffer = ""
        for char in self {
            if delimiters.contains(char) {
                output.append(buffer)
                buffer = String(char)
            } else {
                buffer += String(char)
            }
        }
        output.append(buffer)
        return output
    }
}

编辑2:如果有帮助的话,我正在尝试创建一个SwiftUI版本:https://github.com/optonaut/ActiveLabel.swift

4个回答

3

我不确定您是想要这个还是明确使用 +,但您的目标可以通过以下方式实现

 HStack {
                Text("Hello ")
                Button(action: {
                    // do something
                    print("tapped")
                }, label: {
                    Text("world!").foregroundColor(.blue)
                })
            }

感谢您的回复 - 这不太合适,因为“@”提及或“#”标签按钮将位于文本正文中,而不是单行高度,并且可以出现在任何位置。 - JacobCXDev

3
你可以扩展 AnyView 来获得所需的功能。如果你非常擅长编程,可以为你的目标引入更复杂的自动布局。
extension AnyView{
    static func + (left: AnyView, right: AnyView) -> AnyView{
        return AnyView(HStack{left.fixedSize(horizontal: true, vertical: false)
                              right.fixedSize(horizontal: true, vertical: false)})
    }
}


func styledText(text: String) -> AnyView {
    var output = AnyView(Text(""))
    let components = text.tokenize("@#. ")
    for component in components {
        if component.rangeOfCharacter(from: CharacterSet(charactersIn: "@#")) != nil {
            output = output + AnyView(Text(component).foregroundColor(.accentColor).onTapGesture {
                print(component)
            })
        } else {
            output = output + AnyView(Text(component))
        }
    }

    return output
}

谢谢!如果您将AnyView扩展中的HStack间距设置为0,它几乎可以产生所需的结果!唯一的问题是HStack不会自动换行,因此整个文本段落将在一行上结束,从而破坏视图。有没有什么方法可以克服这个问题或使其发生? - JacobCXDev
所以你需要 fixSize 来测试不同的配置。 - E.Coms

1
这里有另一种解决方案,但仅适用于链接类型的样式。换句话说,如果需要执行任何自定义操作,则我认为没有简单的解决方案。
我的解决方案建议创建一个UIViewRepresentable,可以显示带属性的字符串:
struct AttributedText: UIViewRepresentable {
    
    var attributedString: NSAttributedString
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.attributedText = self.attributedString
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {}
    
}

您可以这样创建一个带属性的字符串:
var attributedString: NSAttributedString = {
    let attrString = NSMutableAttributedString(string: "Hello, ")
    let blueWorld = NSMutableAttributedString(string: "world!")
    let attributes: [NSAttributedString.Key : Any ] = [
        .link: NSURL(string: "https://twitter.com")!
    ]
    let fullRange = NSRange(location: 0, length: blueWorld.length)
    blueWorld.setAttributes(attributes, range: fullRange)
    attrString.append(blueWorld)
    return attrString
}()

或者使用任何好的库,直到苹果提供更好的选项。这样可以让您将文本简单地显示为常规的

0
如果你想将按钮添加到文本元素中,使其成为一个可点击的整体区域,你可以将它们嵌套在一个单独的按钮内,就像这样。
                Button(action: {
                    // Your action here
                    print("Combined button clicked")
                }) {
                    HStack {
                        Text("Don't have an account?")
                            .font(.system(size: 12))

                        Text("Sign up")
                            .font(.system(size: 16))
                    }
                }//SignUp

点击“没有账号?”或“注册”都会触发相同的操作,因为它们是同一个按钮的一部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接