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

时间:2019-11-16 20:11:11

标签: swift swiftui

通常,添加两个Text视图将产生一个Text视图,并保留每个Text视图的属性。例如:

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

这应该产生Hello world!,其中world!为蓝色。现在,如果我想world!可以轻按并充当按钮,该怎么办?嗯,从逻辑上讲,这样做是有道理的:

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

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

编辑:之所以不适合在HStack中使用文本视图和按钮,是因为这是在返回tweet内容的文本视图的函数内:

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 个答案:

答案 0 :(得分:2)

您可以扩展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
            }

答案 1 :(得分:1)

我不确定是要使用还是要明确使用+,但是您可以达到此目标

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

答案 2 :(得分: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
}()

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

struct MyText {

    var body: some View {
        AttributedText(attributedString: myCustomAttributedString)
    }

}

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

答案 3 :(得分:0)

您不能在此处使用+,因为.foregroundColor修饰符会产生some View,但不会产生Text

实际上,您可以使用几乎相同的内容:

var body: some View {
    HStack {
        Text("Hello")
        Text("world!").foregroundColor(.blue).onTapGesture {
            print("world got tapped")
        }
    }
}