如何在 ForEach 上进行单独选择
How to make an individual selection on a ForEach
我正在尝试为用户选择的每张食物图片添加复选标记,但它在每个选项上都被选中,而不是只有一个。
我知道 ForEach
可能是导致此问题的原因。但是我想不出解决这个问题的方法。
The problem on the Simulator.
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom) {
ForEach(comidas) { comida in
VStack(alignment: .center) {
ZStack(alignment: .center) {
Image(uiImage: comida.foodImage)
.resizable()
.blur(radius: 2)
.frame(width: 200, height: 200)
.cornerRadius(10)
.onTapGesture {
withAnimation {
isChecked.toggle()
}
}
Text(comida.name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.white)
Image(systemName: "checkmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(Color.white)
.frame(width: 100, height: 100)
.opacity(isChecked ? 1 : 0 )
.animation(.easeIn(duration: 0.5))
}
}
.padding(.horizontal, 3)
}
}
}
//"comidas" is food in Spanish
struct Comidas: Identifiable {
var id: Int
let name: String
let foodImage: UIImage
}
let comidas = [
Comidas(id: 0, name: "Asado", foodImage: UIImage(imageLiteralResourceName: "asado")),
Comidas(id: 1, name: "Pizzas", foodImage: UIImage(imageLiteralResourceName: "pizzas")),
Comidas(id: 2, name: "Milanesas", foodImage: UIImage(imageLiteralResourceName: "milanesas")),
Comidas(id: 3, name: "Empanadas", foodImage: UIImage(imageLiteralResourceName: "empanadas")),
Comidas(id: 4, name: "Pastas", foodImage: UIImage(imageLiteralResourceName: "pasta")),
Comidas(id: 5, name: "Sushi", foodImage: UIImage(imageLiteralResourceName: "sushi")),
Comidas(id: 6, name: "Facturas", foodImage: UIImage(imageLiteralResourceName: "facturas")),
Comidas(id: 7, name: "Café", foodImage: UIImage(imageLiteralResourceName: "cafe")),
Comidas(id: 8, name: "Helados", foodImage: UIImage(imageLiteralResourceName: "helados"))
]
保留所选 comidas 的参考并使用它来确定 comidas 的已检查状态。在这种情况下,我更喜欢使用 Set
,因为它会自动处理重复项并简化 adding/removing 一个项目。
第一件事是添加 Hashable
一致性到 Comida
以便我们可以用它做 Set
操作。 Comida
结构中的所有属性都已经符合 Hashable
所以声明 Hashable
一致性是你所需要的。
//note that comida should be singular since it represents a single item
struct Comida: Identifiable, Hashable {
在您看来,添加一个 @State
变量来跟踪 user-selected Comida
s
@State var comidasEligidas: Set<Comida> = []
在 comida Image
上的点击手势中,在 comidasEligidas
Set
中添加或删除 comida
.onTapGesture {
if comidasEligidas.contains(comida) {
comidasEligidas.remove(comida)
} else {
comidasEligidas.insert(comida)
}
}
根据集合中是否存在 Comida
切换复选标记的不透明度,并指示观察动画的值是 eligidasComidas
Set
。另外,我建议在复选标记上不允许 hitTesting
,这样它就不会吞下任何触摸。
.opacity(comidasEligidas.contains(comida) ? 1 : 0 )
.animation(.easeIn(duration: 0.5), value: comidasEligidas)
.allowsHitTesting(false)
这是完整的代码片段:
struct ContentView: View {
struct Comida: Identifiable, Hashable {
var id: Int
let name: String
let foodImage: UIImage
}
let comidas = [
Comida(id: 0, name: "Asado", foodImage: UIImage(imageLiteralResourceName: "asado")),
Comida(id: 1, name: "Pizzas", foodImage: UIImage(imageLiteralResourceName: "pizzas")),
Comida(id: 2, name: "Milanesas", foodImage: UIImage(imageLiteralResourceName: "milanesas")),
Comida(id: 3, name: "Empanadas", foodImage: UIImage(imageLiteralResourceName: "empanadas")),
Comida(id: 4, name: "Pastas", foodImage: UIImage(imageLiteralResourceName: "pasta")),
Comida(id: 5, name: "Sushi", foodImage: UIImage(imageLiteralResourceName: "sushi")),
Comida(id: 6, name: "Facturas", foodImage: UIImage(imageLiteralResourceName: "facturas")),
Comida(id: 7, name: "Café", foodImage: UIImage(imageLiteralResourceName: "cafe")),
Comida(id: 8, name: "Helados", foodImage: UIImage(imageLiteralResourceName: "helados"))
]
@State var comidasEligidas: Set<Comida> = []
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom) {
ForEach(comidas) { comida in
VStack(alignment: .center) {
ZStack(alignment: .center) {
Image(uiImage: comida.foodImage)
.resizable()
.blur(radius: 2)
.frame(width: 200, height: 200)
.cornerRadius(10)
.onTapGesture {
if comidasEligidas.contains(comida) {
comidasEligidas.remove(comida)
} else {
comidasEligidas.insert(comida)
}
}
Text(comida.name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.white)
Image(systemName: "checkmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(Color.white)
.frame(width: 100, height: 100)
.opacity(comidasEligidas.contains(comida) ? 1 : 0 )
.animation(.easeIn(duration: 0.5), value: comidasEligidas)
.allowsHitTesting(false)
}
}
.padding(.horizontal, 3)
}
}
}
}
}
我正在尝试为用户选择的每张食物图片添加复选标记,但它在每个选项上都被选中,而不是只有一个。
我知道 ForEach
可能是导致此问题的原因。但是我想不出解决这个问题的方法。
The problem on the Simulator.
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom) {
ForEach(comidas) { comida in
VStack(alignment: .center) {
ZStack(alignment: .center) {
Image(uiImage: comida.foodImage)
.resizable()
.blur(radius: 2)
.frame(width: 200, height: 200)
.cornerRadius(10)
.onTapGesture {
withAnimation {
isChecked.toggle()
}
}
Text(comida.name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.white)
Image(systemName: "checkmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(Color.white)
.frame(width: 100, height: 100)
.opacity(isChecked ? 1 : 0 )
.animation(.easeIn(duration: 0.5))
}
}
.padding(.horizontal, 3)
}
}
}
//"comidas" is food in Spanish
struct Comidas: Identifiable {
var id: Int
let name: String
let foodImage: UIImage
}
let comidas = [
Comidas(id: 0, name: "Asado", foodImage: UIImage(imageLiteralResourceName: "asado")),
Comidas(id: 1, name: "Pizzas", foodImage: UIImage(imageLiteralResourceName: "pizzas")),
Comidas(id: 2, name: "Milanesas", foodImage: UIImage(imageLiteralResourceName: "milanesas")),
Comidas(id: 3, name: "Empanadas", foodImage: UIImage(imageLiteralResourceName: "empanadas")),
Comidas(id: 4, name: "Pastas", foodImage: UIImage(imageLiteralResourceName: "pasta")),
Comidas(id: 5, name: "Sushi", foodImage: UIImage(imageLiteralResourceName: "sushi")),
Comidas(id: 6, name: "Facturas", foodImage: UIImage(imageLiteralResourceName: "facturas")),
Comidas(id: 7, name: "Café", foodImage: UIImage(imageLiteralResourceName: "cafe")),
Comidas(id: 8, name: "Helados", foodImage: UIImage(imageLiteralResourceName: "helados"))
]
保留所选 comidas 的参考并使用它来确定 comidas 的已检查状态。在这种情况下,我更喜欢使用 Set
,因为它会自动处理重复项并简化 adding/removing 一个项目。
第一件事是添加 Hashable
一致性到 Comida
以便我们可以用它做 Set
操作。 Comida
结构中的所有属性都已经符合 Hashable
所以声明 Hashable
一致性是你所需要的。
//note that comida should be singular since it represents a single item
struct Comida: Identifiable, Hashable {
在您看来,添加一个 @State
变量来跟踪 user-selected Comida
s
@State var comidasEligidas: Set<Comida> = []
在 comida Image
上的点击手势中,在 comidasEligidas
Set
.onTapGesture {
if comidasEligidas.contains(comida) {
comidasEligidas.remove(comida)
} else {
comidasEligidas.insert(comida)
}
}
根据集合中是否存在 Comida
切换复选标记的不透明度,并指示观察动画的值是 eligidasComidas
Set
。另外,我建议在复选标记上不允许 hitTesting
,这样它就不会吞下任何触摸。
.opacity(comidasEligidas.contains(comida) ? 1 : 0 )
.animation(.easeIn(duration: 0.5), value: comidasEligidas)
.allowsHitTesting(false)
这是完整的代码片段:
struct ContentView: View {
struct Comida: Identifiable, Hashable {
var id: Int
let name: String
let foodImage: UIImage
}
let comidas = [
Comida(id: 0, name: "Asado", foodImage: UIImage(imageLiteralResourceName: "asado")),
Comida(id: 1, name: "Pizzas", foodImage: UIImage(imageLiteralResourceName: "pizzas")),
Comida(id: 2, name: "Milanesas", foodImage: UIImage(imageLiteralResourceName: "milanesas")),
Comida(id: 3, name: "Empanadas", foodImage: UIImage(imageLiteralResourceName: "empanadas")),
Comida(id: 4, name: "Pastas", foodImage: UIImage(imageLiteralResourceName: "pasta")),
Comida(id: 5, name: "Sushi", foodImage: UIImage(imageLiteralResourceName: "sushi")),
Comida(id: 6, name: "Facturas", foodImage: UIImage(imageLiteralResourceName: "facturas")),
Comida(id: 7, name: "Café", foodImage: UIImage(imageLiteralResourceName: "cafe")),
Comida(id: 8, name: "Helados", foodImage: UIImage(imageLiteralResourceName: "helados"))
]
@State var comidasEligidas: Set<Comida> = []
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom) {
ForEach(comidas) { comida in
VStack(alignment: .center) {
ZStack(alignment: .center) {
Image(uiImage: comida.foodImage)
.resizable()
.blur(radius: 2)
.frame(width: 200, height: 200)
.cornerRadius(10)
.onTapGesture {
if comidasEligidas.contains(comida) {
comidasEligidas.remove(comida)
} else {
comidasEligidas.insert(comida)
}
}
Text(comida.name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.white)
Image(systemName: "checkmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(Color.white)
.frame(width: 100, height: 100)
.opacity(comidasEligidas.contains(comida) ? 1 : 0 )
.animation(.easeIn(duration: 0.5), value: comidasEligidas)
.allowsHitTesting(false)
}
}
.padding(.horizontal, 3)
}
}
}
}
}