Warm tip: This article is reproduced from stackoverflow.com, please click
ios swiftui swiftui-bug

SwiftUI does not evaluate elseif correctly?

发布于 2020-03-31 23:00:39

I am creating a SwiftUI view where I have three possible conditions, and want to show different content based on which condition is met. A simplified example:

struct ContentView: View {

    @State var condition1 = true
    @State var condition2 = false

    var body: some View {
        VStack {
            if condition1 {
                Text("text1")
            } else if condition2 {
                Text("text2")
            } else {
                Text("text3")
            }
        }.onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.condition2 = true
            }

            DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
                self.condition1 = false
            }

            DispatchQueue.main.asyncAfter(deadline: .now() + 6.0) {
                self.condition2 = false
            }
        }
    }
}

What I expect to happen:
1) The UI shows text1 when the app starts
2) The UI still shows text1 after 2 seconds (both variables are true)
3) The UI shows text2 after 4 seconds (condition2 is true but condition1 is now false)
4) The UI shows text3 after 6 seconds (both variables are now false)

What actually happens:
1) The UI shows text1 when the app starts
2) The UI shows text1 after 2 seconds
3) The UI shows text2 after 4 seconds
4) The UI continues to show text2 after 6 seconds

Weirdly enough, if I put breakpoints in the UI code, the line showing text3 is executed, it just seems the UI never updates. Even more weirdly, if I put a Spacer().frame(width: 0, height: 0) in that else case, everything works as expected.

Is this a bug in SwiftUI or what's going on here?

Questioner
BlackWolf
Viewed
55
Asperi 2020-01-31 20:04

Well, actually if to add buttons and switch states on buttons then all works, so this is something with delayed updates (maybe racing, maybe bug... did not dig further)

Here is actually solution, which works (tested with Xcode 11.2 /iOS 13.2) ... separate those conditional dependent views into dedicated view builder, like below

struct TestRefreshOnCondition: View {
    @State var condition1 = true
    @State var condition2 = false

    var ConditionalView: some View {
        Group {
            if condition1 {
                Text("text1")
            } else if condition2 {
                Text("text2")
            } else {
                Text("text3")
            }
        }
    }

    var body: some View {
        VStack {
            ConditionalView
        }.onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.condition2 = true
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
                self.condition1 = false
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 6.0) {
                self.condition2 = false
            }
        }
    }
}