0
votes
test view3 body, glabel selected = false
test view3, will show test view4
test view3 body, glabel selected = false
test view4 init, glabel selected = false, local selected = false
test view4 body, glabel selected = false, local selected = false
test view4, local toggle
test view4 body, glabel selected = false, local selected = true
test view4, global toggle
test view4 body, glabel selected = true, local selected = true
test view3 body, glabel selected = true
test view4 init, glabel selected = true, local selected = true
test view4 body, glabel selected = true, local selected = true
test view4, global toggle
test view4 body, glabel selected = false, local selected = true
test view3 body, glabel selected = false
test view4 init, glabel selected = false, local selected = false  // #1
test view4 body, glabel selected = false, local selected = true   // #2

Here are some logs of source code of following. TestView4 has a local state value that init from global. When global value changed, testView 3&4 both refresh, test view4 init(#1) and test view4 body(#2) have different value, why? The UI of testview4 shows as #2, and I want it be #2, because I don't want global value affect local value once testview4 is showing. How to avoid test view4 init(#1) when global value changed, testview3 refreshes, that cause to test view4 init(#1)?

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

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

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

1 Answers

0
votes

as soon as you insert this line

 @ObservedObject var data = TestData.shared

in a struct, every change in data will trigger an update.

Test yourself:

class T : ObservableObject {

    @Published var a = 1
    @Published var b = 2
}

struct ContentView: View {

    @EnvironmentObject var t : T

    init() {
        print("init")
    }

    var body: some View {
        print ("update")
        return Text("Hello, World!")
    }
}

and the test call in scene delegate:

 var t = T()
        let contentView = ContentView().environmentObject(t)

        Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { (timer) in
            t.a += 1
        }