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

use*_*265 16 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())
    }
}
Run Code Online (Sandbox Code Playgroud)

Joh*_* M. 8

@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())
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:为什么使用索引?

在这个例子中,我们需要从模型中的每个 Item 中获取两件事:

  1. String对价值label的财产,在文本视图中使用。
  2. Binding<Bool>isOn属性,在一个切换视图中使用。

(请参阅我解释绑定的答案。)

我们可以通过直接迭代项目来获取标签值:

ForEach(service.items) { (item: Item) in
    Section(header: Text(item.label)) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

相反, Binding 由 @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")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

所以我们只使用索引。只是为了好的衡量,

ForEach(service.items.indices, id: \.self) { index in
Run Code Online (Sandbox Code Playgroud)

相当于

ForEach(0..<service.items.count, id: \.self) { index in
Run Code Online (Sandbox Code Playgroud)