为 ForEach 循环元素添加动画 (SwiftUI)

Posted

技术标签:

【中文标题】为 ForEach 循环元素添加动画 (SwiftUI)【英文标题】:Add animations to ForEach loop elements (SwiftUI) 【发布时间】:2019-08-20 00:34:45 【问题描述】:

有什么方法可以在 ForEach 循环的元素出现或消失时添加动画?

我尝试过以多种方式使用 withAnimation 和 .animation(),但它们似乎不起作用

这是一些代码(Xcode 11 beta 5):

import SwiftUI

struct test: View 
    @State var ContentArray = ["A","B","C"]
    var body: some View 
        ScrollView
        VStack
            ForEach(ContentArray.indices, id: \.self)index in
                ZStack
                // Object
                    Text(self.ContentArray[index])
                    .frame(width:100,height:100)
                    .background(Color.gray)
                    .cornerRadius(20)
                    .padding()
                //Delete button
                    Button(action: 
                      self.ContentArray.remove(at: index)
                    )
                    Text("✕")
                    .foregroundColor(.white)
                    .frame(width:40,height:40)
                    .background(Color.red)
                    .cornerRadius(100)
                   .offset(x:40,y:-40)
             
           
            
       
   



#if DEBUG
struct test_Previews: PreviewProvider 
    static var previews: some View 
        test()
    

#endif

如下所示,没有动画,一切都感觉非常突然。任何解决方案都非常感谢

重要提示:当元素数量发生变化时,布局应该以与 List 相同的方式发生变化。例如,删除顶部对象时,每个对象都会自动移到顶部

【问题讨论】:

我认为动画只适用于View——就像UIView一样。您是否尝试过使用List 而不是VStack?我很确定动画在那里工作。 (IIRC,堆栈不是视图,只是一种排序视图的方式。) 我刚刚尝试使用 List,是的,它确实有效,谢谢。但是,如果给定选项,我宁愿避免使用 List,因为在设计 UI 时,与堆栈相比,它更受限制。 我明白,但如果我在堆栈中是正确的 - H、V、Z - 不是真正的视图并且动画仅应用于视图(想想@987654330 @) 那么你很可能会被限制在那里。恕我直言,List几乎类似于UITableView。接下来,视图的“层次结构”(想想 ButtonVStack 作为它的标签)被 SwiftUI “自动”缩小为单个 View。但是“堆栈”视图?我真的以为我看到了一些东西表明这是一个......堆栈中的单独视图。 (希望我错了。)您可以使用动画来“显示”视图 - 偏移、滑动、easeInOut。 - 但这似乎有点矫枉过正。祝你好运! 我不知道@dfd 在这里谈论的是什么:“真实观点”。我不相信这是准确的。这是 SwiftUI 中的一个错误。注释掉 ScrollView 和动画应该开始工作。 (我没有尝试您的确切示例,而是制作了自己的最小版本。)我已提交反馈并建议 OP 也这样做。 看起来当视图嵌入到 ForEach 视图时动画不起作用,我希望这只是一个错误 【参考方案1】:

看起来这个问题仍然存在(Xcode 11.4),因为只是复制粘贴观察到的效果是一样的。所以,这里有几个问题:首先,它需要正确设置动画和过渡的组合;其次,ForEach 容器必须知道确切删除了哪个项目,因此必须标识项目,而不是匿名的索引。

因此我们有以下效果(过渡/动画可以是其他):

struct TestAnimationInStack: View 
    @State var ContentArray = ["A","B","C", "D", "E", "F", "G", "I", "J"]
    var body: some View 
        ScrollView
        VStack
            ForEach(Array(ContentArray.enumerated()), id: \.element) (i, item) in // << 1) !
                ZStack
                // Object
                    Text(item)
                    .frame(width:100,height:100)
                    .background(Color.gray)
                    .cornerRadius(20)
                    .padding()
                //Delete button
                    Button(action: 
                       withAnimation  () -> () in              // << 2) !!
                           self.ContentArray.remove(at: i)         
                       
                    )
                    Text("✕")
                    .foregroundColor(.white)
                    .frame(width:40,height:40)
                    .background(Color.red)
                    .cornerRadius(100)
                   .offset(x:40,y:-40)
                .transition(AnyTransition.scale)              // << 3) !!!
           
         
       
   

【讨论】:

感谢您的回答。我现在很忙,但会尽快检查 这很适合移除,但是在执行#append 时,过渡动画不起作用。

以上是关于为 ForEach 循环元素添加动画 (SwiftUI)的主要内容,如果未能解决你的问题,请参考以下文章

Swift 添加一个按钮,从使用 foreach 循环的列表中删除行

如何在 Nightwatch 测试的自定义命令中添加嵌套函数 javascript - forEach - 循环遍历元素

从 foreach 循环的所有元素运行 jquery 函数

swift - 带状态的 ForEach 循环

在forforeach循环体中添加数组元素

Swift 中的 ForEach 循环使用二维数组的内容