带有 ForEach 删除动画的 SwiftUI 自定义列表不起作用 [英] SwiftUI custom list with ForEach delete animation not working

查看:27
本文介绍了带有 ForEach 删除动画的 SwiftUI 自定义列表不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个显示时间信息的自定义列表(分钟和秒,未在下面的代码段中使用以简化代码).当用户向列表中添加条目时,我设法实现了一个不错的动画,但删除条目没有动画(第一个 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆