绑定和状态不会同时更新

时间:2020-12-28 11:27:52

标签: ios swift swiftui

我注意到 SwiftUI 中的一个场景,如果我同时更新 BindingState 变量,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 或将 GeometryReaderContentView 移动到 OtherView,将打印相同的示例:

x1: 0 + x2 = 0 = 0
*** TAP ***
x1: 1 + x2 = 1 = 2

正如预期的那样。我还注意到,通过更改 withAnimation 块中的执行顺序,我可以让 StateBinding 之前更新。

我错过了什么吗?为什么代码会触发两次更新?

1 个答案:

答案 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)
            }
        }
    }

}