SwiftUI中的@Binding和ForEach

sup*_*cio 7 data-binding binding ios swift swiftui

我不能理解如何在SwiftUI @Binding中结合使用ForEach。假设我要Toggle根据布尔数组创建s 的列表。

struct ContentView: View {
    @State private var boolArr = [false, false, true, true, false]

    var body: some View {
        List {
            ForEach(boolArr, id: \.self) { boolVal in
                Toggle(isOn: $boolVal) {
                    Text("IsOn")
                }                
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不知道如何将绑定传递给数组中的布尔对象Toggle。上面的代码给出此错误:

使用未解决的标识符“ $ boolVal”

好的,这对我来说很好(当然)。我试过了:

struct ContentView: View {
    @State private var boolArr = [false, false, true, true, false]

    var body: some View {
        List {
            ForEach($boolArr, id: \.self) { boolVal in
                Toggle(isOn: boolVal) {
                    Text("IsOn")
                }                
            }
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

这次错误是:

在'ForEach'上引用初始化程序'init(_:id:content :)'要求'Binding'符合'Hashable'

有办法解决这个问题吗?

Moj*_*ini 9

?? 不要使用不好的做法!

大多数答案(包括@kontiki 接受的答案)方法会导致引擎在每次更改时重新渲染整个 UI,Apple 在wwdc2021(大约时间 7:40)提到这是一种不好的做法


? 斯威夫特 5.5

在这个版本的 swift 中,您可以通过传入可绑定项直接使用绑定数组元素,例如:

在此处输入图片说明

?? 请注意,iOS 14 及更低版本不支持 Swift 5.5,但至少检查操作系统版本,不要继续这种不良做法!

  • 实际上,这在针对 iOS 14.0 及更高版本时效果很好;刚刚使用 Xcode 13 beta 3 进行了测试。并非所有 Swift 5.5 功能都需要 iOS 15。 (2认同)
  • 我无法让它发挥作用。我得到“无法声明名为 '$direction' 的实体;'$' 前缀保留用于隐式合成的声明``` 和 ```初始化器 'init(_:rowContent:)' 要求 'Binding<[String]>' 符合 $directions 中的 'RandomAccessCollection'``` (2认同)

Pau*_*l B 8

如果您还需要更改Toggles的数量(不仅是它们的值),请考虑这一点。顺便说一句,我们可以省略ForEach{} 块。

struct ContentView: View {
   @State var boolArr = [false, false, true, true, false]

    var body: some View {
        NavigationView {
            // id: \.self is obligatory if you need to insert
            List(boolArr.indices, id: \.self) { idx in
                    Toggle(isOn: self.$boolArr[idx]) {
                        Text(self.boolArr[idx] ? "ON":"OFF")
                }
            }
            .navigationBarItems(leading:
                Button(action: { self.boolArr.append(true) })
                { Text("Add") }
                , trailing:
                Button(action: { self.boolArr.removeAll() })
                { Text("Remove") })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我们可以有类似的东西,但使用 ForEach 而不是列表吗? (2认同)
  • 我们当然可以,@GrandSteph,为什么不呢?`VStack { ForEach(boolArr.indices, id: \.self) { idx in Toggle(isOn: self.$boolArr[idx]) { Text(self.boolArr[idx] ? "ON":"OFF") } } }.padding()`。或者`HStack`,或者`Group`,`Section`等。`List`只有这个简洁的init(包含`ForEach`),因为它是很常见的事情......列出项目。 (2认同)
  • 删除时崩溃是一些人在处理 SwiftUI 时遇到的问题。在某些情况下,使用投影值会有所帮助:`func delete(at offsets: IndexSet) { $store.wrappedValue.data.remove(atOffsets: offsets) // 而不是 store.data.remove() }`。在其他情况下,使用 `Binding()` init (而不是 @Binding 指令)创建 var 是此类问题的最佳解决方案。但这个问题太笼统了,@GrandSteph。如果在这里正确表述,您很可能会得到更好的答案。您还可以在此处检查数组驱动接口的一些变体:/sf/answers/4181798841/ (2认同)

And*_*era 7

在SwiftUI中,仅使用Identifiable结构而不是Bools

struct ContentView: View {
    @State private var boolArr = [BoolSelect(isSelected: true), BoolSelect(isSelected: false), BoolSelect(isSelected: true)]

    var body: some View {
        List {
            ForEach(boolArr.indices) { index in
                Toggle(isOn: self.$boolArr[index].isSelected) {
                    Text("Is on")
                }
            }
        }
    }
}

struct BoolSelect: Identifiable {
    var id = UUID()
    var isSelected: Bool
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我们在“@State”结构中有一个“可识别”数组并且需要对其进行迭代,则该方法将不起作用。如果我们计划插入或删除数组元素,无论如何都必须使用“List(model.boolArr.indices, id: \.self)”。 (2认同)

kon*_*iki 6

您可以使用类似下面的代码。请注意,您将获得不赞成使用的警告,但要解决此问题,请检查其他答案:https : //stackoverflow.com/a/57333200/7786555

import SwiftUI

struct ContentView: View {
    @State private var boolArr = [false, false, true, true, false]

    var body: some View {
        List {
            ForEach(boolArr.indices) { idx in
                Toggle(isOn: self.$boolArr[idx]) {
                    Text("boolVar = \(self.boolArr[idx] ? "ON":"OFF")")
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 使用索引的这一更改也将 ForEach 从显示动态内容更改为静态内容。如果您想从正在迭代的列表中删除一个元素,您可能会收到错误。文档:https://developer.apple.com/documentation/swiftui/foreach/3364099-init (11认同)
  • 扩展 @EmmaKAlexandra 所说的内容 - 如果您正在执行删除操作,您的应用程序将因索引越界错误而崩溃。 (5认同)
  • 不要使用索引。糟糕的代码。Swift 和 SwiftUI 文档中都存在关于此的危险信号。索引会产生错误。Swift 和 SwiftUI 提供了多种其他方式来获取数组成员。谁给了这个绿色复选标记?! (3认同)