Looks like NSTextField
is too slow for work with large attributed texts.
1000 rows with 18 symbols each are slow on M1 processor;
3000 rows slow on macbook pro 2015
Is there exist some component that works fast enough with NSAttributedString?
I need component that will be:
- Fast
- Ability to select/copy text
- Works with NSAttributedString
PS: SwiftUI's Text
with AttributedString
is much slower than NSTextField
with NSAttributedString
Application for testing performance of 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
}
}
CodePudding user response:
Typically large text is stored in an NSTextView, not an NSTextField. But for specialized uses, it's quite common to build your own solutions in Core Text.