SwiftUI中NSViewRepresentable.updateNSView仅在绑定变量被方法内使用时触发是否为预期行为?
SwiftUI中NSViewRepresentable.updateNSView仅在绑定变量被方法内使用时触发是否为预期行为?
嘿,你的猜测完全正确!这确实是SwiftUI的预期行为,刚碰到的时候确实会觉得特别反直觉。
SwiftUI的更新机制是基于依赖追踪的:它会在updateNSView(还有视图的body、updateUIView这类方法)里扫描哪些@State、@Binding这类响应式属性被直接读取,只有当这些被追踪到的依赖发生变化时,才会触发对应的更新方法。
回到你的例子:虽然你的MyNSView里的draw方法用到了myString的绑定,但那属于AppKit的绘制流程,SwiftUI的依赖追踪不会深入到NSView的内部方法里去扫描依赖。如果你的updateNSView里没有直接访问myString,SwiftUI就完全不知道这个方法需要跟着myString的变化而更新,自然就不会调用它了。
至于你用的let _ = myString这种写法,确实有点像临时hack,咱们可以用更符合SwiftUI最佳实践的方式来解决:
更优解决方案:让SwiftUI掌控状态,传递值给NSView
最好的做法是不让NSView直接持有@Binding,而是让SwiftUI负责管理状态,把最新的myString值传递给NSView。这样SwiftUI能明确追踪到依赖,代码也更清晰:
@main struct SwiftUI_exampleApp: App { @State private var myString: String = "Initial value" var body: some Scene { WindowGroup { VStack { MySwiftUIView(myString: $myString) Text("Click me!") .onTapGesture { myString = "Set from Text.onTapGesture" } } } } } struct MySwiftUIView: NSViewRepresentable { @Binding var myString: String func makeNSView(context: Context) -> MyNSView { MyNSView() } func updateNSView(_ nsView: MyNSView, context: Context) { // 直接传递最新值,SwiftUI能明确追踪到myString的依赖 nsView.myString = myString nsView.setNeedsDisplay(nsView.bounds) } } class MyNSView: NSView { // 用普通属性代替Binding,SwiftUI传递最新值 var myString: String = "" { didSet { // 值变化时自动标记需要重绘 setNeedsDisplay(bounds) } } override init(frame frameRect: NSRect) { super.init(frame: frameRect) } override func draw(_ dirtyRect: NSRect) { myString.draw(at: CGPoint(x: 0, y: 0)) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
为什么这个方法更好?
- 符合SwiftUI的设计理念:SwiftUI负责状态管理,AppKit/UIKit视图只负责展示和处理交互,不直接绑定状态。
- 避免了依赖追踪的“盲区”,SwiftUI能准确知道什么时候需要更新NSView。
- 代码更易维护,状态流动更清晰。
总的来说,这种“只追踪直接读取的依赖”是SwiftUI的性能优化手段,它能避免不必要的视图更新。虽然一开始会觉得有点别扭,但遵循SwiftUI的状态管理模式,就能摆脱这类hack写法啦。
备注:内容来源于stack exchange,提问作者arghhh
相关产品推荐
相关产品推荐

