看起来
NSTextField
处理大型属性文本太慢。
1000 行,每行 18 个符号,在 M1 处理器上速度较慢;
2015 年 MacBook Pro 上 3000 行速度慢
是否有一些组件可以与
NSAttributedString
一起运行得足够快?
我需要一个组件:
NSAttributedString
PS:SwiftUI 的
Text
与 AttributedString
比 NSTextField
与 NSAttributedString
慢得多。
NSTextField
性能测试申请:
@main
struct TestAppApp: App {
var body: some Scene {
WindowGroup {
AttrTest()
}
}
}
struct AttrTest: View {
@State var nsString: NSAttributedString = generateText(rows: 1000)
var body: some View{
VStack {
HStack{
Button("1000") {
nsString = generateText(rows: 1000)
}
Button("2000") {
nsString = generateText(rows: 2000)
}
Button("5000") {
nsString = generateText(rows: 5000)
}
Button("7000") {
nsString = generateText(rows: 7000)
}
Button("9000") {
nsString = generateText(rows: 9000)
}
}
TabView {
VStack{
AttributedText(attributedString: $nsString, selectable: false)
}
.tabItem {
Text("NSTextField")
}
AttributedText(attributedString: $nsString, selectable: false)
.padding(.leading, 80)
.background(Color.green)
.tabItem {
Text("Other")
}
}
}
}
}
func generateText(rows: Int) -> NSMutableAttributedString {
let attrs: [[NSAttributedString.Key : Any]] = [
[.foregroundColor: NSColor.red],
[.backgroundColor: NSColor.blue],
[.strokeColor: NSColor.blue],
[.strokeColor: NSColor.green],
[.underlineColor: NSColor.green],
[.underlineColor: NSColor.yellow],
[.underlineColor: NSColor.gray],
[.backgroundColor: NSColor.yellow],
[.backgroundColor: NSColor.green],
[.backgroundColor: NSColor.magenta]
]
let str = NSMutableAttributedString(string: "")
for _ in 0...rows {
let strNew = NSMutableAttributedString(string: "fox jumps over the lazy dog\n")
strNew.setAttributes(attrs.randomElement(), range: NSRange(location: 0, length: strNew.length) )
str.append(strNew)
}
return str
}
@available(OSX 11.0, *)
public struct AttributedText: NSViewRepresentable {
@Binding var text: NSAttributedString
private let selectable: Bool
public init(attributedString: Binding<NSAttributedString>, selectable: Bool = true) {
_text = attributedString
self.selectable = selectable
}
public func makeNSView(context: Context) -> NSTextField {
let textField = NSTextField(labelWithAttributedString: text)
textField.preferredMaxLayoutWidth = textField.frame.width
textField.allowsEditingTextAttributes = true // Fix of clear of styles on click
textField.isSelectable = selectable
return textField
}
public func updateNSView(_ textField: NSTextField, context: Context) {
textField.attributedStringValue = $text.wrappedValue
}
}
通常大文本存储在 NSTextView 中,而不是 NSTextField 中。但对于特殊用途,在 Core Text 中构建自己的解决方案是很常见的。
基于 Rob Napier 答案的代码:
import SwiftUI
import Cocoa
@available(OSX 11.0, *)
public struct AttributedText: View {
@Binding var text: NSAttributedString
public init(attributedString: Binding<NSAttributedString>) {
_text = attributedString
}
public var body: some View {
AttributedTextInternal(attributedString: $text)
.frame(minWidth: $text.wrappedValue.size().width + 350, minHeight: $text.wrappedValue.size().height )
}
}
@available(OSX 11.0, *)
public struct AttributedTextInternal: NSViewRepresentable {
@Binding var text: NSAttributedString
public init(attributedString: Binding<NSAttributedString>) {
_text = attributedString
}
public func makeNSView(context: Context) -> NSTextView {
let textView = NSTextView()
textView.isRichText = true
textView.isSelectable = true
textView.setContent(text: text, makeNotEditable: true)
textView.textStorage
return textView
}
public func updateNSView(_ textView: NSTextView, context: Context) {
textView.setContent(text: text, makeNotEditable: true)
}
}
extension NSTextView {
func setContent(text: NSAttributedString, makeNotEditable: Bool) {
self.isEditable = true
self.selectAll(nil)
self.insertText(text, replacementRange: self.selectedRange())
self.isEditable = !makeNotEditable
}
}