我注意到 SwiftUI
中的一个场景,如果我同时更新 Binding
和 State
变量,View
将分两步重新计算:
我准备了一个简单的虚拟演示来重现这个:
import SwiftUI
struct OtherView: View {
let size: CGSize
@Binding var x1: Int
@State var x2: Int = 0
var body: some View {
Text(text())
.frame(width: size.width,
height: size.height / 2)
.background(Color.blue)
.onTapGesture {
print("*** TAP ***")
withAnimation(Animation.easeIn(duration: 4)) {
x1 += 1
x2 += 1
}
}
}
func text() -> String {
print("x1: \(x1) + x2 = \(x2) = \(x1 + x2)")
return "x1: \(x1) + x2 = \(x2) = \(x1 + x2)"
}
}
struct ContentView: View {
@State private var value: Int = 0
var body: some View {
NavigationView {
GeometryReader { proxy in
OtherView(size: proxy.size,
x1: $value)
}
}
}
}
这个片段打印:
x1: 0 + x2 = 0 = 0
x1: 0 + x2 = 0 = 0
*** TAP ***
x1: 1 + x2 = 0 = 1
x1: 1 + x2 = 1 = 2
这不是我所期望的。如果我删除 NavigationView
或将 GeometryReader
从 ContentView
移动到 OtherView
,将打印相同的示例:
x1: 0 + x2 = 0 = 0
*** TAP ***
x1: 1 + x2 = 1 = 2
正如预期的那样。我还注意到,通过更改 withAnimation
块中的执行顺序,我可以让 State
在 Binding
之前更新。
我错过了什么吗?为什么代码会触发两次更新?
答案 0 :(得分:1)
struct
是不可变的,当您更新具有 SwiftUI 包装器的变量时,您告诉它重新加载整个 View
/struct
。因此,当您更新 x1
时,它会触发重新加载,而当您更新 x2
时,您会触发另一个。
如果您想控制 View
何时重新加载,您必须使用可变/活在 class
中的变量,并且没有用 SwiftUI 包装器包装。
struct OtherView: View {
let size: CGSize
@EnvironmentObject var vm: SyncUpdateViewModel
//@Binding var x1: Int
@State var x2: Int = 0
var body: some View {
Text(text())
.frame(width: size.width,
height: size.height / 2)
.background(Color.blue)
.onTapGesture {
print("*** TAP ***")
withAnimation(Animation.easeIn(duration: 4)) {
vm.x1 += 1
x2 += 1
}
}
}
func text() -> String {
print("x1: \(vm.x1) + x2 = \(x2) = \(vm.x1 + x2)")
return "x1: \(vm.x1) + x2 = \(x2) = \(vm.x1 + x2)"
}
}
class SyncUpdateViewModel: ObservableObject {
var x1: Int = 0
}
struct SyncUpdate: View {
@StateObject var vm: SyncUpdateViewModel = SyncUpdateViewModel()
//@State private var value: Int = 0
var body: some View {
NavigationView {
GeometryReader { proxy in
OtherView(size: proxy.size).environmentObject(vm)
}
}
}
}