如何使 Swift 协调器反映 parent 的绑定更改?
How to make Swift Coordinator reflect parent's bindings changes?
假设我有:
- 结构
Document
,表示文本文档。
EditorView
— NSTextView
,用 Combine 包装,绑定到 Document.content<String>
。
Document
是复杂 store:ObservableObject
的一部分,因此它可以绑定到 EditorView
个实例。
当我第一次创建绑定时,它按预期工作 — 编辑 NSTextView 会更改 Document.content
中的值。
let document1 = Document(...)
let document2 = Document(...)
var editor = EditorView(doc: document1)
但是如果更改绑定到另一个文档...
editor.doc = document2
...然后 updateNSView
可以看到新的 document2
。但是 Coordiantor 内部的 textDidChange
仍然引用 document1
.
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
self.parent.doc.content = textView.string
self.selectedRanges = textView.selectedRanges
}
所以,最初,当我设置新的 bindint 时,NSTextView 将其内容更改为 document2
,但是当我键入时,协调器将更改发送到 document1
。
Coordiantor 是否保留了它自己的 parent
副本,即使 parent 发生变化(@Binding doc
已更新),它仍然引用旧副本?
如何让 Coordinator 反映 parent 的绑定变化?
谢谢!
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
}
}
// ...
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?
一个parent,即这里的EditorView
,是struct,所以答案是肯定的,一般是可以复制的。
How to make Coordinator reflect parent's bindings changes?
而不是(或附加于)父级,将绑定显式注入协调器(通过构造函数)并直接使用它。
假设我有:
- 结构
Document
,表示文本文档。 EditorView
—NSTextView
,用 Combine 包装,绑定到Document.content<String>
。
Document
是复杂 store:ObservableObject
的一部分,因此它可以绑定到 EditorView
个实例。
当我第一次创建绑定时,它按预期工作 — 编辑 NSTextView 会更改 Document.content
中的值。
let document1 = Document(...)
let document2 = Document(...)
var editor = EditorView(doc: document1)
但是如果更改绑定到另一个文档...
editor.doc = document2
...然后 updateNSView
可以看到新的 document2
。但是 Coordiantor 内部的 textDidChange
仍然引用 document1
.
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
self.parent.doc.content = textView.string
self.selectedRanges = textView.selectedRanges
}
所以,最初,当我设置新的 bindint 时,NSTextView 将其内容更改为 document2
,但是当我键入时,协调器将更改发送到 document1
。
Coordiantor 是否保留了它自己的 parent
副本,即使 parent 发生变化(@Binding doc
已更新),它仍然引用旧副本?
如何让 Coordinator 反映 parent 的绑定变化?
谢谢!
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
}
}
// ...
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?
一个parent,即这里的EditorView
,是struct,所以答案是肯定的,一般是可以复制的。
How to make Coordinator reflect parent's bindings changes?
而不是(或附加于)父级,将绑定显式注入协调器(通过构造函数)并直接使用它。