Question: How can I easily change all variables in separate views inside a ForEach statement? (View remarks in code.)
ContentView()
@State var isShowing = false
var body: some View {
Toggle("IsShowing", isOn: $isShowing)
.onChange(of: isShowing) {
if isShowing {
//Set all var `Toggled` in ToggleView() to `true`
} else {
//Set all var `Toggled` in ToggleView() to `false`
}
}
Spacer()
if IsShowing {
ForEach(StaffList, id: \.self) { item in
ToggledView()
}
}
}
ToggledView()
@State var Toggled = false
var body: some View {
if Toggled {
Text("True")
} else {
Text("False")
}
}
If you have any questions, please ask.
CodePudding user response:
The code you presented in your question has some issues and it is not immediately reproducible; let me exemplify:
- You have two different variables,
IsShowing
andisShowing
: is that a typo, or they are supposed to be different?IsShowing
completely hides the list. I will assume that they have different purposes. - It is better to follow the convention in Swift: all variable start lower cased, so avoid
IsShowing
orStaffList
as variable names. - The list in the code above is not visible anyhow because, when you have more than one view in the body, you need to embed them in a container, like a
VStack
. onChange(of:perform:)
takes one parameter in the closure, the code above does not compile - watch out, because SwiftUI throws errors that are misleading in this case.- You want to change all toggles in
ToggledView
: the variabletoggled
could be used for that purpose, but I will assume that it has a local scope, so I added aToggle
in my example below.
Coming to the solution: you need to bind the variable in the parent view with another variable in the child view, then listen to changes in the child variable.
The parent view has an @State
variable, it needs to be passed to a @Binding
variable in the child view. You listen to the changes with .onChange(of:) { }
.
In the code below, each item in the list has their own specific toggle, but when you change the one on the top of the view, all of them will change accordingly.
Parent view:
struct ContentView: View {
let isShowingList = true // This replaces the "IsShowing" variable in the question
@State var isShowing = false
var body: some View {
// Embed content in a container, like VStack
VStack {
Toggle("Toggle all items", isOn: $isShowing)
.padding()
Spacer()
if isShowingList {
ForEach(staffList, id: \.self) { item in
// Pass the variable from this view to the child view
ToggledView(globalToggle: $isShowing)
}
}
Spacer()
}
.padding()
}
}
Child view:
struct ToggledView: View {
// Make state variables private
@State private var toggled = false
// This is where the magic happens: changing iShowing in the parent view
// will change globalToggle in this view
@Binding var globalToggle: Bool
var body: some View {
Toggle(toggled ? "True" : "False", isOn: $toggled)
// .onChange(of:) closure receives one parameter: the variable changed
.onChange(of: globalToggle) { value in
toggled = value
}
}
}