在SwiftUI中更新父视图时如何避免在工作表中调用init()

时间:2020-03-12 19:02:55

标签: swiftui

TestView3可以显示2张纸:TestView4和TestView5。 TestView4具有@State值,该值从TestView3初始化。 TestView5也具有@ObservedObject值,该值也可以从TestView3初始化。

当我切换全局值(global.on)时,它将更新TestView3,然后导致TestView4或TestView5 init()。

Q1。更新TestView3时如何避免在TestView4或TestView5中调用init()?如何完全隔离带有图纸的视图与视图?否则视图中的任何更新都会影响工作表,这太糟糕了。

第二季度。 TestView4或TestView5中init()的行为不同。 TestView4中的本地@State在init()中不会更改。但是本地@ObservedObject将在init()中更改。为什么?好奇怪如何使@ObservedObject不会由于TestView3更新而在init()中更改?

您可以测试以下代码,跟踪日志很奇怪。

class Global: ObservableObject {
    static let shared = Global()
    @Published var on: Bool = false
}

class Local: ObservableObject {
    @Published var on: Bool = false
    init(_ on: Bool) {
        self.on = on
    }
}

struct TestView3: View {
    @ObservedObject var global = Global.shared
    @State private var sheetShowing = false
    @State private var sheetShowingId = 4
    var body: some View {
        print("test view3 body, glabel on = \(global.on)"); return
        VStack {
            Text("Global").foregroundColor(global.on ? .red : .gray).onTapGesture {
                self.global.on.toggle()
            }.padding()
            Button(action: {
                self.sheetShowing = true
                self.sheetShowingId = 4
                print("test view3, will show test view4")
            }) { Text("Show TestView4") }.padding()
            Button(action: {
                self.sheetShowing = true
                self.sheetShowingId = 5
                print("test view3, will show test view5")
            }) { Text("Show TestView5") }.padding()
        }.sheet(isPresented: $sheetShowing) {
            if self.sheetShowingId == 4 {
                TestView4(on: self.global.on)
            } else {
                TestView5(on: self.global.on)
            }
        }
    }
}

struct TestView4: View {
    @State private var on: Bool
    init(on: Bool) {
        self._on = State(initialValue: on)
        print("test view4 init, glabel on = \(Global.shared.on), local on = \(self.on)")
    }
    var body: some View {
        print("test view4 body, glabel on = \(Global.shared.on), local on = \(on)"); return
        VStack {
            Text("TestView4").padding()
            Text("Local").foregroundColor(on ? .red : .gray).onTapGesture {
                self.on.toggle()
                print("test view4, local toggle")
            }.padding()
            Text("Global").foregroundColor(Global.shared.on ? .red : .gray).onTapGesture {
                Global.shared.on.toggle()
                print("test view4, global toggle")
            }.padding()
        }
    }
}

struct TestView5: View {
    @ObservedObject var local: Local
    init(on: Bool) {
        self._local = ObservedObject(initialValue: Local(on))
        print("test view5 init, glabel on = \(Global.shared.on), local on = \(self.local.on)")
    }
    var body: some View {
        print("test view5 body, glabel on = \(Global.shared.on), local on = \(local.on)"); return
        VStack {
            Text("TestView5").padding()
            Text("Local").foregroundColor(local.on ? .red : .gray).onTapGesture {
                self.local.on.toggle()
                print("test view5, local toggle")
            }.padding()
            Text("Global").foregroundColor(Global.shared.on ? .red : .gray).onTapGesture {
                Global.shared.on.toggle()
                print("test view5, global toggle")
            }.padding()
        }
    }
}

3 个答案:

答案 0 :(得分:0)

第一季度:您无法避免。为什么?每次包装在State或ObservedObject中的TestView3属性的值更改时,它都会重新计算其主体。即使TestView3或TestView4不受此值的影响,声明的这一部分

if self.sheetShowingId == 4 {
    TestView4(on: self.global.on)
} else {
    TestView5(on: self.global.on)
}

代表一个视图,其主体必须重新评估。

Q2:这没有简单的答案,只是因为我们对彼此的实现细节不太了解。至少我建议您不要依赖您的调查:-)。大约,很容易看到您在TestView5.init中创建了模型Local()的新(本地副本)

init(on: Bool) {
    self._local = ObservedObject(initialValue: Local(on))
    print("test view5 init, glabel on = \(Global.shared.on), local on = \(self.local.on)")
}

您真的想在每次SwiftUI需要刷新其视图时使用不同的(独立的)模型吗?我不这么认为...

答案 1 :(得分:0)

Apple在iOS14中为此提供了一个新的解决方案:@StateObject,它与@State for Class一样,可安慰ObservableObject。

答案 2 :(得分:-1)

最后,我找到了原因。

@ObservedObject与@State不同。苹果在@State上做了一些特殊的工作。 @State将被保留在独立于视图的特殊空间中。

每次刷新UI时,都会初始化

@ObservedObject。它无法恢复@State之类的值。

因此,使用@State而不是@ObservedObject。