在 iOS 中传统处理字符串样式的方式是使用 NSAttributedString
。但是因为NSAttributedString 是为 UIKit 设计的,在 SwiftUI 中使用很不方便。SwiftUI 的 Text 控件不支持 NSAttributedString,因此要在 SwiftUI 中使用 NSAttributedString,只能通过把文本包在 UILabel 里给 SwiftUI 使用(iOS 15 之前)。
如果使用需要这样通过 UIViewRepresentable
:
swift
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)
}
}
使用的时候这样:
swift
var body: some View {
UIKLabel {
$0.attributedText = NSAttributedString(string: "HelloWorld")
}
}
所以从 iOS 15 开始,SwiftUI 的 Text 终于引入了正经的属性字符串!
Markdown!!!
如果只是常见的基础样式,直接在 Text 中使用 markdown 就可以支持。爷爷都感动的哭了。
swift
struct ContentView: View {
var body: some View {
VStack {
Text("Regular")
Text("*Italics*")
Text("**Bold**")
Text("~Strikethrough~")
Text("`Code`")
Text("[Link](https://apple.com)")
Text("***[They](https://apple.com) ~are~ `combinable`***")
}
}
这些样式可以混合在一起使用。多行的支持也和普通文本一样。
swift
struct MultiMarkdown: View {
var body: some View {
Text("把**加粗**、*Italics* 全放在一起也可以的。`SwiftUI Tips Code`和[链接](https://twitter.com/bestlacklock)~不可以~可以!")
.multilineTextAlignment(.center)
.padding()
.background(Color(uiColor: .secondarySystemBackground))
.cornerRadius(16)
.padding()
}
}
需要说明一下,只有在 Text 中使用 markdown 修饰符才有用,如果只是传入的字符串中有符号是不会触发属性的。其实这样比较合理,否则万一有的文字带两个星号,就意外改变样式会让人很意外。如果要显示字符串中的 markdown,需要使用 AttributedString:
swift
do {
let thankYouString = try AttributedString(
markdown:"**Thank you!** Please visit our [website](https://example.com)")
} catch {
print("Couldn't parse: \(error)")
}
Attributed Strings
iOS 15 开始 Text 支持接受 AttributedString 作为参数,因此可以原生设置属性字符串了:
swift
struct ContentView: View {
var body: some View {
Text(makeAttributedString())
}
func makeAttributedString() -> AttributedString {
var string = AttributedString("Some Attributed String")
string.foregroundColor = .blue
return string
}
}
使用 AttributedString 的一个重要场景就是给部分文字设置属性,AttributedString 也考虑到了这点,良好的支持 range:
swift
struct ContentView: View {
var body: some View {
Text(makeAttributedString())
}
func makeAttributedString() -> AttributedString {
var string = AttributedString("局部 红色 蓝色")
string.foregroundColor = .blue
if let range = string.range(of: "红色") {
string[range].foregroundColor = .red
}
return string
}
}
一个扩展
为了更便捷的支持常用属性字符串,我们可以直接给 Text 增加一个扩展方法。
swift
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`
}
}
如果是给整体设置属性这个扩展还是很实用的:
swift
struct HandyView: View {
var body: some View {
VStack {
Text("SwiftUI Tips") {
$0.font = Font.system(size: 17, design: .monospaced)
}
Text("Strikethrough") {
$0.strikethroughStyle = Text.LineStyle(pattern: .solid, color: .red)
}
Text("Foreground Color") {
$0.foregroundColor = Color.purple
}
}
VStack {
Text("Kern") { $0.kern = CGFloat(10) }
Text("Tracking") { $0.tracking = CGFloat(10) }
}
}
}