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: