SwiftUI Edit Struct из списка

Я пытаюсь создать список, который при нажатии на ячейку меняет hasBeenSeen Bool значение в State сам объект.

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()
        }
    }
}

Моя первоначальная мысль заключается в том, что мне нужно сделать hasBeenSeen к @Statevar, но это, похоже, не работает. Как я могу это сделатьBool val редактируемый из списка?

1 ответ

Представления в SwiftUI неизменяемы - это просто структуры, поэтому вы не можете изменять их свойства. Вот почему в SwiftUI есть концепция@Stateоболочка свойств. Когда вы изменяете свойство "состояние", SwiftUI фактически обновляет значение состояния, а не значение свойства представления (которое, опять же, является неизменным).

Итак, вам нужно установить @State на statesнедвижимость в пределах вашего видения. (Вам также нужно будет изменить имя, поскольку идентификаторState уже занято State оболочка свойств - поэтому я просто изменил ее на StateEntity)

@State var states: [StateEntity] = [
      StateEntity(name: "Oregon", hasBeenSeen: true),
      // ... etc
   ]

Однако этого недостаточно, поскольку, когда вы передаете элемент states массив (a StateEntity value) в дочернее представление, вы просто передаете копию.

Для этого вам понадобится привязка. Привязка позволяет дочерним представлениям изменять свойства состояния родительских представлений без владения данными. Итак, свойство дочернего представления должно использовать@Binding оболочка свойства:

struct StateCell: View {
    @Binding var state: StateEntity

    // ...
}

SwiftUI упростил получение привязки государственного свойства с помощью прогнозируемого значения, которое в данном случае $states.

Однако вам нужно передать привязку не ко всему массиву, а к определенному элементу этого массива. Это, к сожалению (и довольно досадно), немного сложнее. Вам нужно получить индекс элемента и, учитывая индекс, получить доступ к привязке следующим образом:$state[index].

Один из способов - сделать ForEach по индексам states:

var body: some View {
   NavigationView {
      List {
         ForEach(states.indices) { index in
            StateCell(state: self.$states[index])
         }
      }.navigationBarTitle(Text("States"))
   }
}
Другие вопросы по тегам