Используйте оболочку свойства @Published
в своем классе Service
, а не didChange
, и выполняйте итерации по индексам service.items
следующим образом:
struct Item: Identifiable {
var id: String
var label: String
var isOn: Bool {
didSet {
// Added to show that state is being modified
print("\(label) just toggled")
}
}
}
class Service: ObservableObject {
@Published var items: [Item]
init() {
self.items = [
Item(id: "0", label: "Zero", isOn: false),
Item(id: "1", label: "One", isOn: true),
Item(id: "2", label: "Two", isOn: false)
]
}
}
struct MyView: View {
@ObservedObject var service: Service
var body: some View {
List {
ForEach(service.items.indices, id: \.self) { index in
Section(header: Text(self.service.items[index].label)) {
Toggle(isOn: self.$service.items[index].isOn) {
Text("isOn")
}
}
}
}
.listStyle(GroupedListStyle())
}
}
Обновление: почемуиспользовать индексы?
В этом примере нам нужно получить две вещи от каждого элемента в модели:
- Значение
String
свойства label
,для использования в текстовом представлении. - A
Binding<Bool>
из свойства isOn
, для использования в представлении с переключением.
(см. этот ответ где я объясняю Binding.)
Мы могли бы получить значение метки, перебирая элементы напрямую:
ForEach(service.items) { (item: Item) in
Section(header: Text(item.label)) {
...
}
Но структура Item не содержит привязки.Если вы попытаетесь сослаться на Toggle(isOn: item.$isOn)
, вы получите сообщение об ошибке: «Значение типа« Элемент »не имеет члена« $ isOn ».»
Вместо этого привязка предоставляется на верхнем уровнеОболочка свойства @ObservedObject, означающая, что $
должен предшествовать service
.Но если мы начинаем с service
, нам понадобится индекс (и мы не можем объявить промежуточные переменные внутри структуры ForEach, поэтому нам придется вычислять его в строке):
ForEach(service.items) { (item: Item) in
Section(header: Text(item.label)) {
Toggle(isOn: self.$service.items[self.service.items.firstIndex(of: item)!].isOn) {
// This computes the index ^--------------------------------------^
Text("isOn")
}
}
}
Ohи это сравнение, чтобы найти индекс, будет означать, что Item должен соответствовать Equatable.И, самое главное, поскольку мы перебираем все элементы в ForEach, а затем снова в .firstIndex (of :), мы преобразовали наш код из сложности O (n) в O (n ^ 2), то есть он будетработать намного медленнее, когда у нас в массиве большое количество элементов.
Поэтому мы просто используем индексы.На всякий случай,
ForEach(service.items.indices, id: \.self) { index in
эквивалентно
ForEach(0..<service.items.count, id: \.self) { index in