Error: Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'
Error: Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'
我收到上述错误,但不知道如何解决。我有一个包含布尔值的对象数组,需要为每个布尔值显示一个切换。
下面是代码。
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
class 中使用 @Published
属性 包装器,而不是 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())
}
}
更新:为什么要使用索引?
在这个例子中,我们需要从模型中的每个项目中得到两件事:
label
属性 的 String
值,用于文本视图。
- 来自
isOn
属性 的 Binding<Bool>
,用于切换视图。
(参见 我解释绑定的地方。)
我们可以通过直接迭代项目来获取标签值:
ForEach(service.items) { (item: Item) in
Section(header: Text(item.label)) {
...
}
但是 Item 结构不包含绑定。如果您尝试引用 Toggle(isOn: item.$isOn)
,您会得到一个错误:"Value of type 'Item' has no member '$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),这意味着它将运行 当我们在数组中有大量项目时,速度会慢得多。
所以我们只使用索引。只是为了好的措施,
ForEach(service.items.indices, id: \.self) { index in
等同于
ForEach(0..<service.items.count, id: \.self) { index in
我收到上述错误,但不知道如何解决。我有一个包含布尔值的对象数组,需要为每个布尔值显示一个切换。
下面是代码。
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
class 中使用 @Published
属性 包装器,而不是 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())
}
}
更新:为什么要使用索引?
在这个例子中,我们需要从模型中的每个项目中得到两件事:
label
属性 的String
值,用于文本视图。- 来自
isOn
属性 的Binding<Bool>
,用于切换视图。
(参见
我们可以通过直接迭代项目来获取标签值:
ForEach(service.items) { (item: Item) in
Section(header: Text(item.label)) {
...
}
但是 Item 结构不包含绑定。如果您尝试引用 Toggle(isOn: item.$isOn)
,您会得到一个错误:"Value of type 'Item' has no member '$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),这意味着它将运行 当我们在数组中有大量项目时,速度会慢得多。
所以我们只使用索引。只是为了好的措施,
ForEach(service.items.indices, id: \.self) { index in
等同于
ForEach(0..<service.items.count, id: \.self) { index in