SwiftUI - ForEach 删除转换始终仅应用于最后一项 [英] SwiftUI - ForEach deletion transition always applied to last item only
问题描述
我正在尝试向我的 ForEach
添加删除动画,以便其中的每个 Card
在删除时都会向外扩展.这是我目前所拥有的:
I'm trying to add a delete animation to my ForEach
, so that each Card
inside it scales out when removed. This is what I have so far:
问题是无论按下哪个Card
,它总是最后一个动画.有时,每张卡片内的文字都有奇怪的滑动/变形动画.这是我的代码:
The problem is that no matter which Card
is pressed, it's always the last one that animates. And sometimes, the text inside each card has a weird sliding/morphing animation. Here's my code:
/// Ran into this problem: "SwiftUI ForEach index out of range error when removing row"
/// `ObservableObject` solution from https://stackoverflow.com/a/62796050/14351818
class Card: ObservableObject, Identifiable {
let id = UUID()
@Published var name: String
init(name: String) {
self.name = name
}
}
struct ContentView: View {
@State var cards = [
Card(name: "Apple"),
Card(name: "Banana "),
Card(name: "Coupon"),
Card(name: "Dog"),
Card(name: "Eat")
]
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(cards.indices, id: \.self) { index in
CardView(card: cards[index], removePressed: {
withAnimation(.easeOut) {
_ = cards.remove(at: index) /// remove the card
}
})
.transition(.scale)
}
}
}
}
}
struct CardView: View {
@ObservedObject var card: Card
var removePressed: (() -> Void)?
var body: some View {
Button(action: {
removePressed?() /// call the remove closure
}) {
VStack {
Text("Remove")
Text(card.name)
}
}
.foregroundColor(Color.white)
.font(.system(size: 24, weight: .medium))
.padding(40)
.background(Color.red)
}
}
如何扩展被点击的Card
,而不是最后一个?
How can I scale out the Card
that is clicked, and not the last one?
推荐答案
您看到此行为的原因是您使用索引作为 ForEach
的 id
.因此,当从 cards
数组中删除一个元素时,ForEach
看到的唯一区别是最后一个索引消失了.
The reason you're seeing this behavior is because you use an index as an id
for ForEach
. So, when an element is removed from the cards
array, the only difference that ForEach
sees is that the last index is gone.
您需要确保 id
唯一标识 ForEach
的每个元素.
You need to make sure that the id
uniquely identifies each element of ForEach
.
如果您必须使用索引并标识每个元素,您可以使用 enumerated
方法或 zip
将数组及其索引放在一起.我喜欢后者:
If you must use indices and have each element identified, you can either use the enumerated
method or zip
the array and its indices together. I like the latter:
ForEach(Array(zip(cards.indices, cards)), id: \.1) { (index, card) in
//...
}
上面使用对象本身作为ID
,这需要符合Hashable
.如果你不想这样,你可以直接使用 id
属性:
The above uses the object itself as the ID
, which requires conformance to Hashable
. If you don't want that, you can use the id
property directly:
ForEach(Array(zip(cards.indices, cards)), id: \.1.id) { (index, card) in
//...
}
为了完整起见,这里是 enumerated
版本(从技术上讲,它不是索引,而是偏移量,但对于基于 0 的数组,它是相同的):
For completeness, here's the enumerated
version (technically, it's not an index, but rather an offset, but for 0-based arrays it's the same):
ForEach(Array(cards.enumerated()), id: \.1) { (index, card) in
//...
}
这篇关于SwiftUI - ForEach 删除转换始终仅应用于最后一项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!