简体   繁体   中英

Why don't @State parameter changes cause a view update?

I am trying to follow the guidance in a WWDC video to use a @State struct to configure and present a child view. I would expect the struct to be able to present the view, however the config.show boolean value does not get updated when set by the button.

The code below has two buttons, each toggling a different boolean to show an overlay. Toggling showWelcome shows the overlay but toggling config.show does nothing. This seems to be working as intended, I just don't understand why SwiftUI behaves this way. Can someone explain why it's not functioning like I expect, and/or suggest a workaround?

https://developer.apple.com/videos/play/wwdc2020/10040/ @ 5:14

struct InformationOverlayConfig {
    @State var show = false
    @State var title: String?
}

struct InformationOverlay: View {
    
    @Binding var config: InformationOverlayConfig
    
    var body: some View {

        if config.title != nil {
            Text(config.title!)
                .padding()
                .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 15))
        }
    }
}


struct TestView: View {
    
    @State private var configWelcome = InformationOverlayConfig(title: "Title is here")
    @State private var showWelcome = false
    
    
    var body: some View {
        VStack {
            Text("hello world")
            Spacer()
            Button("Toggle struct parameter", action: {
                configWelcome.show.toggle()
            })
            Button("Toggle boolean state", action: {
                showWelcome.toggle()

            })
        }
            .overlay(
                VStack {
                    InformationOverlay(config: $configWelcome).opacity(configWelcome.show ? 1 : 0)
                    InformationOverlay(config: $configWelcome).opacity(showWelcome ? 1 : 0)
                })
    }

You "Config" is not a View . State variables only go in View s.

Also, do not use force unwrapping for config.title . Optional binding or map are the non-redundant solutions.

Lastly, there is no need for config to be a Binding if it actually functions as a constant for a particular view.

struct InformationOverlay: View {
  struct Config {
    var show = false
    var title: String?
  }

  let config: Config

  var body: some View {
    VStack {
      if let title = config.title {
        Text(title)
          .padding()
          .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 15))
      }
      // or
      config.title.map {
        Text($0)
          .padding()
          .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 15))
      }
    }
  }
}
struct TestView: View {
  @State private var configWelcome = InformationOverlay.Config(title: "Title is here")
  @State private var showWelcome = false

  var body: some View {
    VStack {
      Text("hello world")
      Spacer()
      Button("Toggle struct parameter") {
        configWelcome.show.toggle()
      }
      Button("Toggle boolean state") {
        showWelcome.toggle()
      }
    }
    .overlay(
      VStack {
        InformationOverlay(config: configWelcome).opacity(configWelcome.show ? 1 : 0)
        InformationOverlay(config: configWelcome).opacity(showWelcome ? 1 : 0)
      }
    )
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM