在 SwiftUI 中将视图添加到层​​次结构时如何动画转换

How to animate transition when adding view to hierarchy in SwiftUI

我正在尝试构建“弹出窗口”视图的叠加层并制作进出动画。转出有效,但转入无效——添加弹出视图时它会突然出现(错误),但当删除弹出视图时它会向右滑出(正确)。在此代码中,当将弹出窗口添加到视图层次结构时,如何使弹出窗口滑入(从右侧)?

iOS14.

中的完整功能代码
import SwiftUI

struct ContentView: View {
    
    var body: some View {
        Popovers()
    }
}

struct Popovers : View {
    
    @State var popovers : [AnyView] = []
    
    var body : some View {
        Button("Add a view ...") {
            withAnimation {
                popovers += [new()]
            }
        }
        .blur(radius: 0 < popovers.count ? 8 : 0)
        .overlay(ZStack {
            ForEach(0..<self.popovers.count, id: \.self) { i in
                popovers[i]
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .blur(radius: (i+1) < popovers.count ? 8 : 0)
                    .transition(.move(edge: .trailing)) // works only when popover is removed
            }
        })
    }
    
    func new() -> AnyView {
        let popover = popovers.count
        
        return AnyView.init(
            VStack(spacing: 64) {
                
                Button("Close") {
                    withAnimation {
                        _ = popovers.removeLast()
                    }
                }
                .font(.largeTitle)
                .padding()

                Button("Add") {
                    withAnimation {
                        popovers += [new()]
                    }
                }
                .font(.largeTitle)
                .padding()

                Text("This is popover #\(popover)")
                    .font(.title)
                    .foregroundColor(.white)
                    .fixedSize()
                
            }
            .background(Color.init(hue: 0.65-(Double(3*popover)/100.0), saturation: 0.3, brightness: 0.9).opacity(0.98))
        )
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension View {
    var asAnyView : AnyView {
        AnyView(self)
    }
}

解决方案是向容器中添加动画。测试 Xcode 12 / iOS 14.

struct Popovers : View {
    
    @State var popovers : [AnyView] = []
    
    var body : some View {
        Button("Add a view ...") {
            withAnimation {
                popovers += [new()]
            }
        }
        .blur(radius: 0 < popovers.count ? 8 : 0)
        .overlay(ZStack {
            ForEach(0..<self.popovers.count, id: \.self) { i in
                popovers[i]
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .blur(radius: (i+1) < popovers.count ? 8 : 0)
                    .transition(.move(edge: .trailing))
            }
        }.animation(.default))    // << add animation to container
    }
    
    func new() -> AnyView {
        let popover = popovers.count
        
        return AnyView.init(
            VStack(spacing: 64) {
                
                Button("Close") {
                            _ = popovers.removeLast()
                }
                .font(.largeTitle)
                .padding()

                Button("Add") {
                            popovers += [new()]
                }
                .font(.largeTitle)
                .padding()

                Text("This is popover #\(popover)")
                    .font(.title)
                    .foregroundColor(.white)
                    .fixedSize()
                
            }
            .background(Color.init(hue: 0.65-(Double(3*popover)/100.0), saturation: 0.3, brightness: 0.9).opacity(0.98))
        )
    }
    
}