NSViewRepresentable.updateNSView 不会被调用,除非修改后的绑定变量在内部使用

问题描述 投票:0回答:1

这是 SwiftUI 的预期行为吗?我可能对 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 {
        let myView = MyNSView(myString: $myString)
        return myView
    }

    func updateNSView(_ nsView: MyNSView, context: Context) {
        // this. If something like this line is not included, updateNSView is not called when myString changes
        let _ = myString

        nsView.setNeedsDisplay(NSRect(x: 0, y: 0, width: nsView.frame.width, height: nsView.frame.height))
    }
}
class MyNSView: NSView {
    @Binding var myString: String

    init(myString: Binding<String>) {
        self._myString = myString
        super.init(frame: NSRect.zero)
    }

    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“智能”地检测 updateNSView() 内部使用了哪些绑定变量,并且仅在其中一个变量发生更改时才调用它;然而,在像上面例子这样间接使用变量的情况下(即在 NSView.draw() 内部,在 nsView.setNeedsDisplay() 调用之后调用),它不够智能,无法正确检测,并且 updateNSView() 是跳过。

我有这个权利吗?那条

let _ = myString
线有点可怕,我想相信这不是最好/正确的方法。

swift cocoa swiftui uikit appkit
1个回答
4
投票

我的猜测是,SwiftUI“智能”地检测内部使用了哪些绑定变量

updateNSView()
,并且仅在其中一个变量发生更改时才调用它

这基本上是正确的。

这里的错误是你不应该在

@Binding
子类中有
NSView

class MyNSView: NSView {
    var myString: String

    init(myString: String) {
        self.myString = myString
        super.init(frame: NSRect.zero)
    }

    ...

我假设您在

@Binding
中使用了
MyNSView
,因为您希望这将使
MyNSView.myString
MySwiftUIView.myString
“同步”。这是不正确的,因为
@Binding
在“SwiftUI 世界”之外不起作用。

保持这些同步正是

updateNSView
的目的。您应该在
MyNSView.myString
中更新
updateNSView

NSViewRepresentable
的实现应该如下所示:

struct MySwiftUIView: NSViewRepresentable {

    @Binding var myString: String

    func makeNSView(context: Context) -> MyNSView {
        let myView = MyNSView(myString: $myString)
        return myView
    }

    func updateNSView(_ nsView: MyNSView, context: Context) {
        // Note this line:
        nsView.myString = myString
        // This is how you keep the myString in MyNSView synchronised with myString in MySwiftUIView

        nsView.setNeedsDisplay(nsView.bounds)
    }
}

如果您不打算更改

myString
中的
MySwiftUIView
,您甚至不需要
@Binding
中的
MySwiftUIView
。只需将
myString
声明为
let
常量即可。

© www.soinside.com 2019 - 2024. All rights reserved.