错误:初始化程序"init(_ :)"要求"Binding< String>"符合'StringProtocol' [英] Error: Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'

查看:176
本文介绍了错误:初始化程序"init(_ :)"要求"Binding< String>"符合'StringProtocol'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am getting the above error and couldn't figure out how to solve it. I have an array of objects that contain a boolean value, and need to show a toggle for each of these boolean.

下面是代码.

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

推荐答案

Service 类中使用 @Published 属性包装,而不是 didChange ,并像这样遍历 service.items 的索引:

Use the @Published property wrapper in your Service class, rather than didChange, and iterate over the indices of service.items like so:

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

更新:为什么要使用索引?

在此示例中,我们需要从模型中的每个项目中获取两件事:

In this example, we need to get two things from each Item in the model:

  1. label 属性的 String 值,用于文本视图.
  2. 来自 isOn 属性的 Binding< Bool> ,用于Toggle视图.
  1. The String value of the label property, to use in a Text view.
  2. A Binding<Bool> from the isOn property, to use in a Toggle view.

(请参阅此答案,在此我将解释绑定.)

(See this answer where I explain Binding.)

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

We could get the label value by iterating over the items directly:

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

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

But the Item struct does not contain a binding. If you tried to reference Toggle(isOn: item.$isOn), you'd get an error: "Value of type 'Item' has no member '$isOn'."

相反,@ ObservedObject属性包装器在顶层提供了绑定,这意味着 $ 必须在 service 之前.但是,如果我们从 service 开始,则需要一个索引(而且我们无法在ForEach结构中声明中间变量,因此我们必须内联计算):

Instead, the Binding is provided at the top level by the @ObservedObject property wrapper, meaning the $ has to come before service. But if we're starting from service, we'll need an index (and we cannot declare intermediate variables inside the ForEach struct, so we'll have to compute it inline):

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时,运行速度会慢得多.

Oh, and that comparison to find the index would mean Item has to conform to Equatable. And, most importantly, because we are looping over all items in the ForEach, and then again in the .firstIndex(of:), we have transformed our code from O(n) complexity to O(n^2), meaning it will run much more slowly when we have a large number of Items in the array.

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

So we just use the indices. Just for good measure,

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

等效于

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

这篇关于错误:初始化程序"init(_ :)"要求"Binding&lt; String&gt;"符合'StringProtocol'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆