如何在NSViewRepresentable中监听@Observable类的变更
监听AnnotationsCoordinator变更的几种实现方案
方案1:利用SwiftUI的updateNSView自动触发更新
因为@Observable类的属性变更会触发SwiftUI视图重计算,进而自动调用updateNSView方法。你只需要在这个方法里把最新的注释数据传递给MyTextView并触发重绘即可:
func updateNSView(_ view: NSView, context: Context) { if let textView = context.coordinator.textView as? MyTextView { // 同步最新的注释数组 textView.annotations = annotationsCoordinator.annotations // 触发视图重绘 textView.setNeedsDisplay(textView.bounds) } }
这个方案最简洁,不需要额外的监听逻辑,依赖SwiftUI自身的状态更新机制即可完成同步。
方案2:在自定义Coordinator中订阅属性变更
如果需要更精细的控制(比如只监听annotations的变化,而非整个对象的任意变更),可以在MyViewCoordinator中使用ObservationTracker来订阅目标属性:
修改MyViewCoordinator的实现:
class MyViewCoordinator { let parent: MyCustomTextView let annotationsCoordinator: AnnotationsCoordinator private var observationToken: ObservationToken? init(_ parent: MyCustomTextView, _ annotationsCoordinator: AnnotationsCoordinator) { self.parent = parent self.annotationsCoordinator = annotationsCoordinator // 订阅annotations属性的变更 observationToken = withObservationTracking { // 必须显式访问要监听的属性,否则无法触发回调 _ = self.annotationsCoordinator.annotations } onChange: { [weak self] in guard let self = self, let textView = self.parent.context?.coordinator.textView as? MyTextView else { return } textView.annotations = self.annotationsCoordinator.annotations textView.setNeedsDisplay(textView.bounds) } } }
注意用weak self避免循环引用,同时确保MyViewCoordinator持有annotationsCoordinator的强引用。
方案3:让MyTextView直接监听变更
如果MyTextView需要独立响应注释变化,可以让它直接持有AnnotationsCoordinator的引用并订阅变更:
class MyTextView: NSTextView { var annotations: [Annotation] = [] private var observationToken: ObservationToken? func bindAnnotationsCoordinator(_ coordinator: AnnotationsCoordinator) { observationToken = withObservationTracking { _ = coordinator.annotations } onChange: { [weak self] in guard let self = self else { return } self.annotations = coordinator.annotations self.setNeedsDisplay(self.bounds) } } }
然后在makeNSView方法中完成绑定:
func makeNSView(context: Context) -> NSView { let textView = MyTextView() textView.bindAnnotationsCoordinator(annotationsCoordinator) context.coordinator.textView = textView return textView }
关键注意事项
- 父视图中必须用
@State持有AnnotationsCoordinator实例,这样SwiftUI才能捕获到它的属性变更。 - 使用
withObservationTracking时,必须在闭包内显式访问要监听的属性(比如coordinator.annotations),否则无法触发变更回调。 - UI更新操作默认在主线程执行,无需额外处理。
内容的提问来源于stack exchange,提问作者koen
相关产品推荐
相关产品推荐

