Post

Replies

Boosts

Views

Activity

Reply to TextKit 2 + SwiftUI (NSViewRepresentable): NSTextLayoutManager rendering attributes don’t reliably draw/update
Thanks for the quick response. I’d prefer to use a pure SwiftUI text solution, but at the moment I need TextKit 2 for features SwiftUI still doesn’t expose—real-time syntax highlighting, paragraphStyle handling, tracking visibleRect changes, and related editor behaviors. Below is a minimal example showing how I’m using NSTextLayoutManager rendering attributes for a dynamic highlight. import SwiftUI struct RATestView: View { @State private var text = """ TextKit 2 rendering highlight demo. Type something and watch highlight update. """ @State private var search = "highlight" var body: some View { VStack { TextField("Search", text: $search) WrapperView(text: $text, highlight: search) .frame(height: 300) } } } private struct WrapperView: NSViewRepresentable { @Binding var text: String var highlight: String func makeNSView(context: Context) -> CustomTextView { let view = CustomTextView() return view } func updateNSView(_ nsView: CustomTextView, context: Context) { nsView.setText(text) nsView.setHighlight(highlight) } } private final class CustomTextView: NSView { private let textView = NSTextView() override init(frame frameRect: NSRect) { super.init(frame: frameRect) setupView() } required init?(coder: NSCoder) { super.init(coder: coder) setupView() } private func setupView() { addSubview(textView) textView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ textView.leadingAnchor.constraint(equalTo: leadingAnchor), textView.trailingAnchor.constraint(equalTo: trailingAnchor), textView.topAnchor.constraint(equalTo: topAnchor), textView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } func setText(_ string: String) { if textView.string != string { textView.string = string } } func setHighlight(_ highlightString: String) { guard let documentRange = textView.textLayoutManager?.documentRange else { return } textView.textLayoutManager?.invalidateRenderingAttributes(for: documentRange) let highlightRange = (textView.string as NSString).range(of: highlightString) guard let range = convertToTextRange(textView: textView, range: highlightRange) else { return } textView.textLayoutManager?.addRenderingAttribute(.backgroundColor, value: NSColor(.red), for: range) textView.textLayoutManager?.invalidateLayout(for: range) textView.needsDisplay = true textView.needsLayout = true } } private func convertToTextRange(textView: NSTextView, range: NSRange) -> NSTextRange? { guard let textLayoutManager = textView.textLayoutManager, let textContentManager = textLayoutManager.textContentManager, let start = textContentManager.location(textContentManager.documentRange.location, offsetBy: range.location), let end = textContentManager.location(start, offsetBy: range.length) else { return nil } return NSTextRange(location: start, end: end) }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
4h