Looks like NSTextField
is too slow to 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 some component that works fast enough with NSAttributedString
?
I need a component that will be:
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
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With