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

Posted

技术标签:

【中文标题】是否可以在 SwiftUI 中将 Button<Text> 添加到 Text 中?【英文标题】:Is it possible to add Button<Text> to Text in SwiftUI? 【发布时间】:2019-11-16 20:11:11 【问题描述】:

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

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

这应该产生Hello world!,蓝色的world!。现在,如果我想让world! 可点击并充当按钮,我该怎么办?好吧,从逻辑上讲,这样做是有意义的:

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

但是,直接上面的代码无法编译。是否可以按照所述创建带有可点击部分的文本视图?我的用例是用于带有 @ 提及或 # 主题标签的推文,您可以在其中点击提及或主题标签以显示详细视图。

编辑:在 HStack 中使用 Text 视图和 Button 不合适的原因是因为这是在返回推文内容的 Text 视图的函数中:

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。

【问题讨论】:

【参考方案1】:

您可以扩展 AnyView 以获得您需要的内容。如果你非常擅长编程,你可以为你的目标引入更复杂的自动布局。

extension AnyView
    static func + (left: AnyView, right: AnyView) -> AnyView
        return AnyView(HStackleft.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 没有包装它的内容,所以整个文本段落将在一行结束,打乱了视图。有什么办法可以克服或实现它? 所以你需要 fixSize 来测试不同的配置【参考方案2】:

我不确定你是想要这个还是你想明确地使用 +,但你可以通过这个来达到你的目标

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

【讨论】:

感谢您的回复 — 这不合适,因为 @ 提及或 # 主题标签按钮将位于正文中,高度超过一行,并且可能是所述文本中的任何位置。【参考方案3】:

这是另一种解决方案,但这仅对链接类型的样式真正有用。换句话说,如果需要,我认为没有简单的解决方案可以执行任何自定义操作。

我的解决方案建议创建一个可以显示属性字符串的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
()

或使用任何好的库,直到苹果有更好的选择。这使您可以简单地显示文本,因为它是常规的Text

struct MyText 

    var body: some View 
        AttributedText(attributedString: myCustomAttributedString)
    


当您只对链接感兴趣并且可以接受链接的默认显示(据我所知,您无法更改此设置)时,此解决方案具有潜力。

【讨论】:

以上是关于是否可以在 SwiftUI 中将 Button<Text> 添加到 Text 中?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中将 DatePicker 放入 ContextMenu

如何在 SwiftUI 中将图像设置为按钮?

SwiftUI 的 List/ForEach 视图中是不是可以有一个 Button(action: -)

如何在swiftui中将vstack移动到屏幕顶部?

SwiftUI button 与sheet的使用

如何在 SwiftUI 中将内容添加到收藏列表?