onthemoon
onthemoon

Reputation: 3462

State variable value is being lost

Im have the following code running in ios 1. When the button "View A" is pressed the state variable nextModalView2 is set to NextModalView2.a But When the Sheet() call is executed the value of nextModalView2 is reset to .none.

How is it possible?

Alsoif you look at the screenshot, you will see that the debugger console panel is showing a value for nextModalView2 that is different from what shown in the variable panel of the debugger. How is that?

enter image description here


enum NextModalView2{
  case none
  case a
  case b
}

struct Menu2: View {
  @State private var isShowModally = false
  @State private var nextModalView2 = NextModalView2.none

  var body: some View {
    VStack(alignment: .center) {
      Button(action: {
        self.isShowModally = true
        self.nextModalView2 = .a
        print("self.nextModalView = \(self.nextModalView2)")
      }){ Text("View A")
      }
      Divider()
      Button(action: {
        self.isShowModally = true
        self.nextModalView2 = .b
      }){ Text("View B")
      }
      .padding(.top, 20)
    }
    .sheet(isPresented:  $isShowModally){
      Group{
        if self.nextModalView2 == .a {
          //        case NextModalView2.a:
          AnyView(Text("A"))
        }
        if  self.nextModalView2 == NextModalView2.b{
          AnyView(Text("B"))
        }
        if self.nextModalView2 == NextModalView2.none{
          AnyView(EmptyView())
        }
      }
    }
  }
}

Upvotes: 4

Views: 756

Answers (2)

Asperi
Asperi

Reputation: 257663

We need to use sheet(item: modifier in such scenario, it works per-selection.

Here is a solution. Tested with Xcode 12.1 / iOS 14.1.

demo

enum NextModalView2: Identifiable{
    var id: NextModalView2 { self }
    case none
    case a
    case b
}

struct Menu2: View {
    @State private var nextModalView2: NextModalView2?
    
    var body: some View {
        VStack(alignment: .center) {
            Button(action: {
                self.nextModalView2 = .a
                print("self.nextModalView = \(self.nextModalView2 ?? .none)")
            }){ Text("View A")
            }
            Divider()
            Button(action: {
                self.nextModalView2 = .b
            }){ Text("View B")
            }
            .padding(.top, 20)
        }
        .sheet(item: $nextModalView2){ item in
            switch item {
            case .a:
                Text("A")
            case .b:
                Text("B")
            default:
                EmptyView()
            }
        }
    }
}

Upvotes: 1

jnpdx
jnpdx

Reputation: 52367

My experience using sheets with SwiftUI is that you can get some really funny behaviors (especially on Catalyst). My suspicion is that the sheet gets rendered at first run (when your value is none) and then doesn't actually get called to update when your @State variable changes like it should, probably because under the covers, the sheet is wrapped in something like a UIViewControllerRepresentable and the values aren't getting passed when you think they will.

I extracted your sheet view and changed the parameter to a @Binding, even though you may not need two-way communication, since it seems to fix this behavior:


enum NextModalView2 {
  case none
  case a
  case b
}

struct ContentView: View {
  @State private var isShowModally = false
  @State private var nextModalView2 = NextModalView2.none
    
  var body: some View {
    VStack(alignment: .center) {
      Button(action: {
        self.nextModalView2 = .a
        self.isShowModally = true
        print("self.nextModalView = \(self.nextModalView2)")
      }){ Text("View A")
      }
      Divider()
      Button(action: {
        self.nextModalView2 = .b
        self.isShowModally = true
      }){ Text("View B")
      }
      .padding(.top, 20)
    }
    .sheet(isPresented:  $isShowModally){
        SheetContent(nextModalView2: $nextModalView2)
    }
  }
}

struct SheetContent : View {
    @Binding var nextModalView2 : NextModalView2
    
    var body: some View {
        Group{
          if self.nextModalView2 == .a {
            AnyView(Text("A"))
          }
          if  self.nextModalView2 == NextModalView2.b{
            AnyView(Text("B"))
          }
          if self.nextModalView2 == NextModalView2.none {
            AnyView(EmptyView())
          }
        }
    }
}

Upvotes: 0

Related Questions