使用 NSAttributedString 的最快组件?
Fastest component that works with NSAttributedString?
看起来 NSTextField
对于大型属性文本的处理速度太慢了。
1000 rows with 18 symbols each are slow on M1 processor;
3000 rows slow on macbook pro 2015
是否存在一些组件可以足够快地使用 NSAttributedString?
我需要的组件将是:
- 快
- 能够select/copy文本
- 适用于 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
}
}
看起来 NSTextField
对于大型属性文本的处理速度太慢了。
1000 rows with 18 symbols each are slow on M1 processor;
3000 rows slow on macbook pro 2015
是否存在一些组件可以足够快地使用 NSAttributedString?
我需要的组件将是:
- 快
- 能够select/copy文本
- 适用于 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
}
}