I am attempting to have a list that when a cell it tapped it changes the hasBeenSeen
Bool
value within the State
object itself.
struct State: Identifiable {
var id = UUID()
let name: String
var hasBeenSeen: Bool = false
}
struct ContentView: View {
let states: [State] = [
State(name: "Oregon", hasBeenSeen: true),
State(name: "California", hasBeenSeen: true),
State(name: "Massachussets", hasBeenSeen: false),
State(name: "Washington", hasBeenSeen: true),
State(name: "Georgia", hasBeenSeen: false)
]
var body: some View {
NavigationView {
List {
ForEach(states, id: \.id) { state in
StateCell(state: state)
}
}.navigationBarTitle(Text("States"))
}
}
}
struct StateCell: View {
var state: State
var body: some View {
HStack {
Text(state.name)
Spacer()
if state.hasBeenSeen {
Image(systemName: "eye.fill")
}
}.onTapGesture {
// state.hasBeenSeen.toggle()
}
}
}
My original thought is that I need to make hasBeenSeen
to a @State
var but that doesn't seem to work. How can I make this Bool
val editable from a list?
Views in SwiftUI are immutable - they are just structures - so you can't change their properties. That's why SwiftUI has a concept of a
@State
property wrapper. When you change "state" property, SwiftUI actually updates the state value, not the view's property value (which is, again, immutable).So, you need to set
@State
on thestates
property within your view. (You'd also need to change the name, since identifierState
is already taken by theState
property wrapper - so I just changed it toStateEntity
)That's not enough, though, since when you pass an element of the
states
array (aStateEntity
value) to a child view, you're just passing a copy.For that, you'd need a binding. A binding allows child views to modify state properties of parent views, without owning the data. So, the child view's property should use the
@Binding
property wrapper:SwiftUI made it easy to get the binding of state property by using the projected value, which in this case is
$states
.However, you need to pass a binding not to the entire array, but to a specific element of that array. That, unfortunately (and rather annoyingly), is a bit trickier. You need to get the index of the element, and given the index, access the binding like so:
$state[index]
.One way is to do a
ForEach
over indices ofstates
: