错误:初始化程序“ init(_ :)”要求“ Binding <String>”符合“ StringProtocol”

时间:2019-09-23 17:01:51

标签: swift swiftui combine

我遇到了以上错误,无法解决。我有一个包含布尔值的对象数组,并且需要显示每个布尔值的切换。

下面是代码。

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 个答案:

答案 0 :(得分:1)

@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. 来自Binding<Bool>属性的isOn,用于Toggle视图。

(请参阅this answer,其中解释了绑定。)

我们可以通过直接遍历项目来获得标签值:

ForEach(service.items) { (item: Item) in
    Section(header: Text(item.label)) {
    ...
}

但是Item结构不包含绑定。如果您尝试引用Toggle(isOn: item.$isOn),则会收到错误消息:“类型'Item'的值没有成员'$ 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")
        }
    }
}

哦,通过比较发现索引将意味着Item必须符合Equatable。而且,最重要的是,因为我们要遍历ForEach中的所有项,然后再遍历.firstIndex(of :),所以我们已将代码从O(n)复杂度转换为O(n ^ 2),这意味着它将当数组中有大量Items时,运行速度会慢得多。

所以我们只使用索引。出于良好的考虑,

ForEach(service.items.indices, id: \.self) { index in

等效于

ForEach(0..<service.items.count, id: \.self) { index in