Let's say I have:
- structure
Document
, which represents text document. EditorView
— anNSTextView
, wrapped with Combine, which binds toDocument.content<String>
.
Document
is a part of complex store:ObservableObject
, so it can be bouneded to EditorView
instance.
When I first create binding, it works as expected — editing NSTextView changes value in Document.content
.
let document1 = Document(...)
let document2 = Document(...)
var editor = EditorView(doc: document1)
But if change binding to another Document...
editor.doc = document2
...then updateNSView
can see new document2
. But inside Coordiantor's textDidChange
has still refrence to document1
.
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
self.parent.doc.content = textView.string
self.selectedRanges = textView.selectedRanges
}
So, initially, when i set new bindint, NSTextView changes it content to document2
, but as I type, coordinator sends changes to document1
.
Is it true, that Coordiantor keeps it's own copy of parent
, and even if parent changes (@Binding doc
is updated), it still references to old one?
How to make Coordinator reflect parent's bindings changes?
Thank you!
struct Document: Identifiable, Equatable {
let id: UUID = UUID()
var name: String
var content: String
}
struct EditorView: NSViewRepresentable {
@Binding var doc: Document
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeNSView(context: Context) -> CustomTextView {
let textView = CustomTextView(
text: doc.content,
isEditable: isEditable,
font: font
)
textView.delegate = context.coordinator
return textView
}
func updateNSView(_ view: CustomTextView, context: Context) {
view.text = doc.content
view.selectedRanges = context.coordinator.selectedRanges
}
}
// MARK: - Coordinator
extension EditorView {
class Coordinator: NSObject, NSTextViewDelegate {
var parent: EditorView
var selectedRanges: [NSValue] = []
init(_ parent: EditorView) {
self.parent = parent
}
func textDidBeginEditing(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
self.parent.doc.content = textView.string
self.parent.onEditingChanged()
}
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
self.parent.doc.content = textView.string
self.selectedRanges = textView.selectedRanges
}
func textDidEndEditing(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
self.parent.doc.content = textView.string
self.parent.onCommit()
}
}
}
// MARK: - CustomTextView
final class CustomTextView: NSView {
private var isEditable: Bool
private var font: NSFont?
weak var delegate: NSTextViewDelegate?
var text: String {
didSet {
textView.string = text
}
}
// ...