如何在SwiftUI中使用属性字符串

时间:2019-12-30 12:48:18

标签: swiftui

如何在SwiftUI中使用AttributedString。没有可用的API  在文本中使用AttributedString

5 个答案:

答案 0 :(得分:2)

attributed string的思想是带有属性的字符串。在SwiftUI中,此想法是通过Text属性修饰符和+运算符实现的。如以下示例所示:

SwiftUI Text with attributes

Group {
    Text("Bold")
        .fontWeight(.bold) +
    Text("Underlined")
        .underline() +
    Text("Color")
        .foregroundColor(Color.red)
}

答案 1 :(得分:2)

iOS 15(测试版)

我们终于得到了AttributedString!它真的很容易使用。

Multiple Attributed Strings with different attributes

struct ContentView: View {
    var body: some View {
        VStack(spacing: 40) {
            
            /// Note: You can replace `$0` with `string in string`
            
            VStack {
                Text("Regular")
                Text("Italics") { $0.font = Font.system(size: 17).italic() }
                Text("Bold") { $0.font = Font.system(size: 17).bold() }
                Text("Strikethrough") { $0.strikethroughColor = Color.red }
                Text("Code") { $0.font = Font.system(size: 17, design: .monospaced) }
                Text("Foreground Color") { $0.foregroundColor = Color.purple }
                Text("Background Color") { $0.backgroundColor = Color.yellow }
                Text("Underline") { $0.underlineColor = Color.green }
            }
            
            VStack {
                Text("Kern") { $0.kern = CGFloat(10) }
                Text("Tracking") { $0.tracking = CGFloat(10) }
            }
            
            VStack {
                Text("Baseline Offset") { $0.baselineOffset = CGFloat(10) }
                Text("Link") { $0.link = URL(string: "https://apple.com") }
            }
        }
    }
}

/// extension to make applying AttributedString even easier
extension Text {
    init(_ string: String, configure: ((inout AttributedString) -> Void)) {
        var attributedString = AttributedString(string) /// create an `AttributedString`
        configure(&attributedString) /// configure using the closure
        self.init(attributedString) /// initialize a `Text`
    }
}

要将属性应用于特定范围,请使用 range(of:options:locale:) 方法。

Attributed String with different colors

struct ContentView: View {
    var body: some View {
        Text("Some Attributed String") { string in
            string.foregroundColor = .blue
            if let range = string.range(of: "Attributed") { /// here!
                string[range].foregroundColor = .red
            }
        }
    }
}

有关详细信息,请参阅我的 article。此外,您还可以use Markdown

答案 2 :(得分:1)

它不直接在Text上支持,但是您可以将UILabel带到那里并以自己喜欢的方式对其进行修改:

实施:

struct Label: UIViewRepresentable {

    typealias TheUIView = UILabel
    fileprivate var configuration = { (view: TheUIView) in }

    func makeUIView(context: UIViewRepresentableContext<Self>) -> TheUIView { TheUIView() }
    func updateUIView(_ uiView: TheUIView, context: UIViewRepresentableContext<Self>) {
        configuration(uiView)
    }
}

用法:

var body: some View {
    Label {
        $0.attributedText = NSAttributedString(string: "HelloWorld")
    }
}

更多本地用户,但访问较少:

您还可以将多个Text对象组合在一起,这将处理某些属性:

Text("Hello") + Text("World")

每个人可以有多个特定的修饰符

答案 3 :(得分:1)

有许多答案都使用UILabelUITextView。我很好奇是否有可能创建一个不依赖任何UIKit功能的本机SwiftUI实现。这代表了适合我当前需求的实现。它是NSAttributedString规范的完整实现中的FAR,但对于大多数基本需求而言,它绝对足够了。使用HTML字符串的NSAttributedString的构造函数是我制作的自定义类别,非常易于实现。如果有人想使用它并创建一个更强大且更完整的组件,那么您将成为我的英雄。可悲的是我没有时间进行这样的项目。

//
//  AttributedText.swift
//
import SwiftUI

struct AttributedTextBlock {
    let content: String
    let font: Font?
    let color: Color?
}

struct AttributedText: View {
    var attributedText: NSAttributedString?
    
    private var descriptions: [AttributedTextBlock] = []
    
    init(_ attributedText: NSAttributedString?) {
        self.attributedText = attributedText
        
        self.extractDescriptions()
    }
    
    init(stringKey: String) {
        self.init(NSAttributedString(htmlString: NSLocalizedString(stringKey, comment: "")))
    }
    
    init(htmlString: String) {
        self.init(NSAttributedString(htmlString: htmlString))
    }
    
    private mutating func extractDescriptions()  {
        if let text = attributedText {
            text.enumerateAttributes(in: NSMakeRange(0, text.length), options: [], using: { (attribute, range, stop) in
                let substring = (text.string as NSString).substring(with: range)
                let font =  (attribute[.font] as? UIFont).map { Font.custom($0.fontName, size: $0.pointSize) }
                let color = (attribute[.foregroundColor] as? UIColor).map { Color($0) }
                descriptions.append(AttributedTextBlock(content: substring,
                                                        font: font,
                                                        color: color))
            })
        }
    }
    
    var body: some View {
        descriptions.map { description in
            Text(description.content)
                .font(description.font)
                .foregroundColor(description.color)
        }.reduce(Text("")) { (result, text) in
            result + text
        }
    }
}

struct AttributedText_Previews: PreviewProvider {
    static var previews: some View {
        AttributedText(htmlString: "Hello! <b>World</b>")
    }
}

答案 4 :(得分:1)

如果你想用 NSAttributedString 实现动态高度文本,你可以使用这个:

实施:

struct TextWithAttributedString: View {

var attributedText: NSAttributedString
@State private var height: CGFloat = .zero

var body: some View {
    InternalTextView(attributedText: attributedText, dynamicHeight: $height)
        .frame(minHeight: height)
}

struct InternalTextView: UIViewRepresentable {

    var attributedText: NSAttributedString
    @Binding var dynamicHeight: CGFloat

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.textAlignment = .justified
        textView.isScrollEnabled = false
        textView.isUserInteractionEnabled = false
        textView.showsVerticalScrollIndicator = false
        textView.showsHorizontalScrollIndicator = false
        textView.allowsEditingTextAttributes = false
        textView.backgroundColor = .clear
        textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        textView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
        return textView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.attributedText = attributedText
        DispatchQueue.main.async {
            dynamicHeight = uiView.sizeThatFits(CGSize(width: uiView.bounds.width, height: CGFloat.greatestFiniteMagnitude)).height
        }
    }
}

}

用法:

    VStack {
       TextWithAttributedString(attributedText: viewModel.description)
         .padding([.leading, .trailing], self.horizontalPadding)
         .layoutPriority(1)
         .background(Color.clear)
    }
    .transition(.opacity)
    .animation(.linear)