Ошибка: инициализатор 'init (_ :)' требует, чтобы 'Binding <String>' соответствовал 'StringProtocol' - PullRequest
0 голосов
/ 23 сентября 2019

Я получаю вышеуказанную ошибку и не могу понять, как ее решить.У меня есть массив объектов, которые содержат логическое значение, и мне нужно показать переключатель для каждого из этих логических значений.

Ниже приведен код.

class Item: Identifiable {
    var id: String
    var label: String
    var isOn: Bool
}

class Service: ObservableObject {
    var didChange = PassthroughSubject<Void, Never>()

    var items: [Item] {
        didSet {
            didChange.send(())
        }
    }
}

struct MyView: View {
    @ObservedObject var service: Service

    var body: some View {
        List {
            ForEach(service.items, id: \.self) { (item: Binding<Item>) in
                Section(header: Text(item.label)) {  // Error: Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'
                    Toggle(isOn: item.isOn) {
                        Text("isOn")
                    }
                }
            }
        }
        .listStyle(GroupedListStyle())
    }
}

1 Ответ

1 голос
/ 24 сентября 2019

Используйте оболочку свойства @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())
    }
}

Обновление: почемуиспользовать индексы?

В этом примере нам нужно получить две вещи от каждого элемента в модели:

  1. Значение String свойства label,для использования в текстовом представлении.
  2. 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
...