与 NSAttributedString 配合使用最快的组件?

问题描述 投票:0回答:2

看起来

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
    }
}
swift performance swiftui nsattributedstring
2个回答
4
投票

通常大文本存储在 NSTextView 中,而不是 NSTextField 中。但对于特殊用途,在 Core Text 中构建自己的解决方案是很常见的。


0
投票

基于 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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.