[英]@Binding and ForEach in SwiftUI
I can't undertand how to use @Binding<\/code> in combination with
ForEach<\/code> in SwiftUI.
我不能undertand如何使用
@Binding<\/code>结合
ForEach<\/code>在SwiftUI。
Let's say I want to create a list of
Toggle<\/code> s from an array of booleans.
假设我想从一个布尔数组创建一个
Toggle<\/code>列表。
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("Is \(boolVal ? "On":"Off")")
}
}
}
}
}
You can use something like the code below.您可以使用类似下面的代码。 Note that you will get a deprecated warning, but to address that, check this other answer: https://stackoverflow.com/a/57333200/7786555请注意,您将收到已弃用的警告,但要解决此问题,请查看其他答案: 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")")
}
}
}
}
}
In SwiftUI, just use Identifiable structs instead of Bools在 SwiftUI 中,只需使用Identifiable structs 而不是 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(self.boolArr[index].isSelected ? "ON":"OFF")
}
}
}
}
}
struct BoolSelect: Identifiable {
var id = UUID()
var isSelected: Bool
}
If also you need to change the number of Toggle
s (not only their values), consider this.如果您还需要更改Toggle
的数量(不仅是它们的值),请考虑这一点。 BTW, we can omit ForEach
{} block.顺便说一句,我们可以省略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") })
}
}
}
Most of the answers (including the @kontiki accepted answer) method cause the engine to rerender the entire UI on each change and Apple mentioned this as a bad practice at wwdc2021 (around time 7:40)大多数答案(包括@kontiki 接受的答案)方法会导致引擎在每次更改时重新渲染整个 UI,Apple 在wwdc2021 (大约时间 7:40)提到这是一种不好的做法
From this version of swift, you can use binding array elements directly by passing in the bindable item like:在这个版本的 swift 中,您可以通过传入可绑定项直接使用绑定数组元素,例如:
⚠️ Note that Swift 5.5 is not supported on iOS 14 and below but at least check for the os version and don't continue the bad practice! ⚠️请注意,iOS 14 及更低版本不支持 Swift 5.5,但至少检查操作系统版本,不要继续这种不良做法!
In WWDC21 videos Apple clearly stated that using .indices
in the ForEach
loop is a bad practice.在 WWDC21 视频中,Apple 明确表示在ForEach
循环中使用.indices
是一种不好的做法。 Besides that, we need a way to uniquely identify every item in the array, so you can't use ForEach(boolArr, id:\\.self)
because there are repeated values in the array.除此之外,我们需要一种方法来唯一标识数组中的每个项目,因此您不能使用ForEach(boolArr, id:\\.self)
因为数组中有重复的值。
As @Mojtaba Hosseini stated, new to Swift 5.5 you can now use binding array elements directly passing the bindable item.正如@Mojtaba Hosseini 所说,Swift 5.5 的新手现在可以使用绑定数组元素直接传递可绑定项。 But if you still need to use a previous version of Swift, this is how I accomplished it:但是,如果您仍然需要使用以前版本的 Swift,我就是这样做的:
struct ContentView: View {
@State private var boolArr: [BoolItem] = [.init(false), .init(false), .init(true), .init(true), .init(false)]
var body: some View {
List {
ForEach(boolArr) { boolItem in
makeBoolItemBinding(boolItem).map {
Toggle(isOn: $0.value) {
Text("Is \(boolItem.value ? "On":"Off")")
}
}
}
}
}
struct BoolItem: Identifiable {
let id = UUID()
var value: Bool
init(_ value: Bool) {
self.value = value
}
}
func makeBoolItemBinding(_ item: BoolItem) -> Binding<BoolItem>? {
guard let index = boolArr.firstIndex(where: { $0.id == item.id }) else { return nil }
return .init(get: { self.boolArr[index] },
set: { self.boolArr[index] = $0 })
}
}
First we make every item in the array identifiable by creating a simple struct conforming to Identifiable
.首先,我们通过创建一个符合Identifiable
的简单结构来使数组中的每一项都可Identifiable
。 Then we make a function to create a custom binding.然后我们创建一个函数来创建自定义绑定。 I could have used force unwrapping to avoid returning an optional from the makeBoolItemBinding
function but I always try to avoid it.我本可以使用强制解包来避免从makeBoolItemBinding
函数返回一个可选项,但我总是尽量避免它。 Returning an optional binding from the function requires the map method to unwrap it.从函数返回一个可选绑定需要 map 方法来解包它。
I have tested this method in my projects and it works faultlessly so far.我已经在我的项目中测试了这种方法,到目前为止它工作得很好。
如何在可绑定数组中进行 .filtering?
"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.