I have the following Swift view:
我有以下几点看法:
struct GameView: View {
@State private var userWon: Bool = false {
didSet {
print("userWon: \(userWon)") // not called
// TODO: Change game state here
}
}
var body: some View {
ZStack {
VStack {
GameViewRepresentable(userWon: $userWon)
.onChange(of: userWon) { newValue in
print("GameViewRepresentable: \(newValue)") // called
// TODO: or change game state here?
}
}
.clipped()
}
}
}
The view representable is like so:
可表示的视图如下所示:
struct GameViewRepresentable: UIViewRepresentable {
typealias UIViewType = GameView
@Binding var userWon: Bool {
willSet { newValue
print("willSet userWon: \(newValue)")
}
didSet {
print("didSet userWon: \(userWon)")
}
}
func makeUIView(context: Context) -> GameView {
let gameView = GameView()
gameView.delegate = context.coordinator
return gameView
}
func updateUIView(_ uiView: GameView, context: Context) {
}
func makeCoordinator() -> GameViewCoordinator {
GameViewCoordinator(self)
}
class GameViewCoordinator: GameViewDelegate {
var parent: GameViewRepresentable
init(_ parent: GameViewRepresentable) {
self.parent = parent
}
func userWon() {
self.parent.userWon = true
}
}
}
I can see that the userWon
method is correctly getting called inside the coordinator which correspondingly calls willSet
and didSet
in GameViewRepresentable
, but the didSet
in the GameView
for the binding userWon
property is never called. However the onChange
in GameView does get called. So is that the right way to respond to changes to the state property? I was expecting the didSet
inside the GameView to get called instead.
我可以看到,在协调器内部正确地调用了userWon方法,该协调器相应地调用了GameView Renatable中的will Set和didSet,但绑定的userWon属性的Gameview中的didSet从未被调用。然而,Gameview中的onChange确实会被调用。那么,这是应对国有资产变化的正确方式吗?我原以为会调用Gameview中的didSet。
更多回答
Yes, onChange
is how you detect @State
changes.
是的,onChange是您检测@State更改的方式。
It is perfectly normal for willSet
and didSet
to not be triggered on a @State
or @Binding
. Swift inserts calls to willSet
and didSet
in the setter of the userWon
property, so if userWon
changes not by calling userWon
's setter, then willSet
and didSet
will not be called.
在@State或@绑定上不触发will Set和didSet是非常正常的。SWIFT在userWon属性的setter中插入对will Set和didSet的调用,因此如果没有通过调用userWon的setter来更改userWon,则不会调用will Set和didSet。
Your userWon
property lowers to something like this:
您的userWon属性的值如下所示:
private var _userWon = State(initialValue: false)
private var userWon: Bool {
get { _userWon.wrappedValue }
set {
// <willSet is called here>
_userWon.wrappedValue = newValue
// <didSet is called here>
}
}
private var $userWon: Binding<Bool> {
_userWon.projectedValue
}
Consider this really simple example:
考虑一下这个非常简单的例子:
@State private var userWon: Bool = false {
willSet {
print("willSet")
}
didSet {
print("didSet")
}
}
var body: some View {
Toggle(isOn: $userWon) {
Text("Foo")
}
}
No matter how many times I toggle the toggle, willSet
and didSet
are not called. This is because the Toggle
doesn't actually change the value of userWon
by literally saying e.g. userWon = true
(which would have called its setter). Toggle
doesn't know that userWon
exists at all.
无论我切换开关多少次,都不会调用will Set和didSet。这是因为Togger实际上并没有通过字面意思改变userWon的值,例如,userWon=True(这将调用它的setter)。Togger根本不知道userWon的存在。
What actually happens is you passed a Binding<Bool>
to the Toggle
, which was the projectedValue
of the State
. $userWon
is just a short way of saying _userWon.projectedValue
. The Toggle
will at some point set the wrappedValue
property of the Binding<Bool>
. This will then cause the wrappedValue
of the State
to be set, and the view gets updated.
实际发生的情况是,您将一个绑定
传递给了Togger,这是State的ProjectedValue。$userWon只是_userWon.project tedValue的缩写。Togger将在某个时刻设置绑定
的wrapedValue属性。然后,这将导致设置State的wrappeValue,并更新视图。
Notice that everything is done through the wrappedValue
properties of State
and Binding
, not your userWon
property.
请注意,所有操作都是通过State和Binding的wrapedValue属性完成的,而不是通过您的userWon属性。
You need to implement updateUIView
, e.g.
您需要实现更新UIView,例如
func updateUIView(_ uiView: GameView, context: Context) {
context.coordinator.parent = self
}
Note, it would be better practice to set new closures on the coordinator instead.
注意,更好的做法是在协调器上设置新的闭包。
更多回答
it's a mistake, sometimes those tutorials are written by interns
这是个错误,有时这些教程是实习生写的
Fruta PaymentButton has done in a different way that also works
Fruta PaymentButton以一种不同的方式做到了这一点
It is just a style choice like many other things, I’ve said it before my only issue with some of your posts is the absoluteness of your choice of words.
这只是一种风格选择,就像许多其他事情一样,我在我对你的一些帖子唯一的问题是你的措辞的绝对性之前已经说过了。
我是一名优秀的程序员,十分优秀!