【发布时间】:2020-04-19 05:12:10
【问题描述】:
如何在 SwiftUI 中使用 AttributedString。没有可用的 API 在文本中使用 AttributedString
【问题讨论】:
如何在 SwiftUI 中使用 AttributedString。没有可用的 API 在文本中使用 AttributedString
【问题讨论】:
Text现在支持markdown,你也可以创建自定义属性:
您甚至可以远程获取已定义的属性,例如:
您可以将多个 Text 对象与一个简单的 + 运算符组合在一起,这将处理一些属性:
每一个都可以有多个特定的修饰符
由于它不直接支持Text(直到 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")
}
}
【讨论】:
some View 而不是 Text?
Label 的结构。您可能忘记实现自己的 Label 并且它与原始版本冲突 @Ixx
UIKLabel 上启用对齐?已尝试在堆栈上包装并拥有 View 似乎没有任何效果。
attributed string 的思想是带有属性的字符串。在 SwiftUI 中,这个想法是通过 Text 属性修饰符和 + 运算符实现的。如下例所示:
Group {
Text("Bold")
.fontWeight(.bold) +
Text("Underlined")
.underline() +
Text("Color")
.foregroundColor(Color.red)
}
【讨论】:
有很多答案都使用UILabel 或UITextView。我很好奇是否可以创建一个不依赖任何 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>")
}
}
【讨论】:
如果你想用 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)
【讨论】:
我们终于得到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!
【讨论】:
要为 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)
}
}
}
【讨论】:
Text(someInstanceOf_AttributedString) 快得多
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
}
}
【讨论】: