带有 ForEach 删除动画的 SwiftUI 自定义列表不起作用 [英] SwiftUI custom list with ForEach delete animation not working
问题描述
我正在创建一个显示时间信息的自定义列表(分钟和秒,未在下面的代码段中使用以简化代码).当用户向列表中添加条目时,我设法实现了一个不错的动画,但删除条目没有动画(第一个 GIF).
I am creating a custom list displaying time information (minutes and seconds, not used in the snippet below to simplify the code). I managed to implement a nice animation when the user adds an entry to the list, but deleting an entry has no animation (1st GIF).
在 iOS 14 中,动画可以正常工作,但是,动画只会从列表中删除最后一个矩形,然后更新每行中的文本(第二个 GIF).这不是我想要的 - 我的目标是如果删除了一行,其他行应该填满该空间并相应地移动 - 带有动画.
With iOS 14, the animation is working, however, the animation only removes the last rectangle from the list and then updates the text in each row (2nd GIF). That's not what I want - My goal is if a row has been deleted, the other rows should fill up that space and move accordingly - with an animation.
行的 ID 可能有问题,但我无法修复.感谢您的帮助!
Probably something is wrong with the IDs of the rows but I just wasn't able to fix that. Thanks for helping!
struct ContentView: View {
@State var minutes = [0]
@State var seconds = [0]
@State var selectedElement = 0
var body: some View {
ScrollView(){
VStack{
ForEach(minutes.indices, id: \.self){ elem in
ZStack{
EntryBackground()
Text("\(self.minutes[elem])")
.transition(AnyTransition.scale)
HStack{
Button(action: {
withAnimation(.spring()){
self.seconds.remove(at: elem)
self.minutes.remove(at: elem)
}
})
{
Image(systemName: "minus.circle.fill")
.foregroundColor(Color.red)
.font(.system(size: 22))
.padding(.leading, 10)
}
Spacer()
}
}
.padding(.horizontal)
.padding(.top)
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring()){
self.selectedElement = elem
}
}
}
}
Spacer()
Button(action: {
withAnimation{
self.minutes.append(self.minutes.count)
self.seconds.append(0)
}
})
{
ZStack{
EntryBackground()
Text("Add")
HStack{
Image(systemName: "plus.circle.fill")
.foregroundColor(Color.green)
.font(.system(size: 22))
.padding(.leading, 10)
Spacer()
}
}.padding()
}
}
}
}
struct EntryBackground: View {
var body: some View {
Rectangle()
.cornerRadius(12)
.frame(height: 40)
.foregroundColor(Color.gray.opacity(0.15))
}
}
推荐答案
您需要使每一行都具有唯一标识,以便动画师知道添加了哪些内容以及删除了哪些内容,从而正确地为每个更改设置了动画.
You need to make each row uniquely identified, so animator know what is added and what is removed, so animate each change properly.
这是可能的方法.使用 Xcode 12/iOS 14 测试
Here is possible approach. Tested with Xcode 12 / iOS 14
struct TimeItem: Identifiable, Equatable {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id
}
let id = UUID() // << identify item
let minutes: Int
let seconds: Int = 0
}
struct ContentView: View {
@State var items = [TimeItem]()
@State var selectedElement: TimeItem?
var body: some View {
ScrollView(){
VStack{
ForEach(items){ elem in // << work by item
ZStack{
EntryBackground()
Text("\(elem.minutes)")
.transition(AnyTransition.scale)
HStack{
Button(action: {
self.items.removeAll { $0.id == elem.id }
})
{
Image(systemName: "minus.circle.fill")
.foregroundColor(Color.red)
.font(.system(size: 22))
.padding(.leading, 10)
}
Spacer()
}
}
.padding(.horizontal)
.padding(.top)
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring()){
self.selectedElement = elem
}
}
}
}
Spacer()
Button(action: {
self.items.append(TimeItem(minutes: self.items.count))
})
{
ZStack{
EntryBackground()
Text("Add")
HStack{
Image(systemName: "plus.circle.fill")
.foregroundColor(Color.green)
.font(.system(size: 22))
.padding(.leading, 10)
Spacer()
}
}.padding()
}
}.animation(.spring(), value: items) // << animate changes
}
}
struct EntryBackground: View {
var body: some View {
Rectangle()
.cornerRadius(12)
.frame(height: 40)
.foregroundColor(Color.gray.opacity(0.15))
}
}
这篇关于带有 ForEach 删除动画的 SwiftUI 自定义列表不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!