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

wyyhbhjk  于 2023-01-29  发布在  Swift
关注(0)|答案(7)|浏览(371)

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

rpppsulh

rpppsulh1#

iOS 15和Swift 5.5
Text现在支持markdown,您还可以创建自定义属性:

您甚至可以远程获取定义的属性,例如:

iOS 13和14
你可以用一个简单的+操作符将多个Text对象组合在一起,这样就可以处理一些属性:

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

完全支持的回退!

由于Text不直接支持UILabel(直到iOS 15),您可以将UILabel带到那里,并根据自己的喜好进行修改:

实施:

struct UIKLabel: 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 {
    UIKLabel {
        $0.attributedText = NSAttributedString(string: "HelloWorld")
    }
}
s2j5cfk0

s2j5cfk02#

attributed string的概念是 * 字符串与属性 *。在SwiftUI中,此概念通过Text属性修饰符和+运算符实现。如以下示例所示:

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

ar5n3qh53#

iOS 15操作系统

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

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.strikethroughStyle = Text.LineStyle(pattern: .solid, 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:)方法。

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

ocebsuys

ocebsuys4#

有很多答案都使用UILabelUITextView。我很好奇是否有可能创建一个不依赖于任何UIKit功能的原生SwiftUI实现。这代表了一个满足我当前需求的实现。它距离NSAttributedString规范的完整实现还很远。但对于最基本的需求来说,它绝对足够好了。接受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>")
    }
}
zzlelutf

zzlelutf5#

如果希望使用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)
svmlkihl

svmlkihl6#

为了只为iOS 14添加一种不同的风格,这对我很有效:

struct ItalicTextView: View {
  let text: String
  let italicText: String

  var body: some View {
    let array = text.components(separatedBy: italicText)
    array.reduce(Text(""), {
      if $1 == array.last {
        return $0 + Text($1)
      }
      return $0 + Text($1) + Text(italicText).italic()
    })
  }
}

用法:

var body: some View {
 HStack(alignment: .center, spacing: 0) {
    ItalicTextView(text: notification.description, italicText: "example")
      .multilineTextAlignment(.leading)
      .fixedSize(horizontal: false, vertical: true)
      .padding(.vertical, 16)
      .padding(.horizontal, 8)
  }
}

}

hpcdzsge

hpcdzsge7#

1.适用于MacOS
1.比SwiftUI的Text(someInstanceOf_AttributedString)快得多
1.点击或选择文本时无需重置字体属性即可选择文本

import SwiftUI
import Cocoa

@available(OSX 11.0, *)
public struct AttributedText: NSViewRepresentable {
    private let text: NSAttributedString
    
    public init(attributedString: NSAttributedString) {
        text = attributedString
    }
    
    public func makeNSView(context: Context) -> NSTextField {
        let textField = NSTextField(labelWithAttributedString: text)
        textField.isSelectable = true
        textField.allowsEditingTextAttributes = true // Fix of clear of styles on click
        
        textField.preferredMaxLayoutWidth = textField.frame.width
        
        return textField
    }
    
    public func updateNSView(_ nsView: NSTextField, context: Context) {
        nsView.attributedStringValue = text
    }
}

相关问题