如何在使用 SwiftUI .move 转换/动画时剪辑视图

How to clip a view while using a SwiftUI .move transition / animation

我试图在一个视图中设置动画,让它看起来像是从另一个视图打开的抽屉。这一切都很好,除非第一个视图不是不透明的。看起来你可以在它开始动画的那一刻看到动画视图。有没有办法剪裁它,使视图看起来是从底部视图的顶部增长的?

即使没有不透明度,如果您制作动画的位置未被覆盖(在第二个 gif 中演示),这也是一个问题

示例代码:

struct ContentView: View {
    @State private var showingSecondView: Bool = false
    

    var body: some View {
        VStack(spacing: 0) {
            Spacer()
            if showingSecondView {
                ZStack {
                    Color.green.opacity(0.25)
                    Text("Second View")
                }
                .frame(width: 300, height: 300)
                .transition(.move(edge: .bottom))
            }
            ZStack {
                Color.black.opacity(1)
                Text("First View")
            }
            .frame(width: 300, height: 300)
            Button("Animate In / Out") {
                showingSecondView.toggle()
            }
            .padding()
        }
        .animation(.easeInOut, value: showingSecondView)
      }
}

这里有一个适合你的方法:

 struct ContentView: View {
    
    @State private var isSecondViewPresented: Bool = false

    var body: some View {
        
        VStack(spacing: 0) {
            Spacer()
            
            ZStack {
                Color.green.opacity(0.25).cornerRadius(20)
                Text("Second View")
            }
            .frame(width: 300, height: 300)
            .offset(y: isSecondViewPresented ? 0 : 300)
            .clipShape(RoundedRectangle(cornerRadius: 20))
            
            
            ZStack {
                Color.black.opacity(0.1).cornerRadius(20)
                Text("First View")
            }
            .frame(width: 300, height: 150)
            
            Button("Animate In / Out") {
                isSecondViewPresented.toggle()
            }
            .padding()
        }
        .animation(.easeInOut, value: isSecondViewPresented)
        
    }

}

可以通过精确剪裁 'drawer' 的容器来实现。这是一个可能的方法演示。

使用 Xcode 13.2 / iOS 15.2 进行测试(为了更好的演示,模拟器慢速动画开启)

var body: some View {
    VStack(spacing: 0) {
        Spacer()
        VStack {
            if showingSecondView {
                ZStack {
                    Color.green.opacity(0.25)
                    Text("Second View")
                }
                .transition(.move(edge: .bottom))
            } else {
                Color.clear // << replacement for transition visibility
            }
        }
        .frame(width: 300, height: 300)
        .animation(.easeInOut, value: showingSecondView)  // << animate drawer !!
        .clipped()            // << clip drawer area
        
        ZStack {
            Color.black.opacity(0.2)
            Text("First View")
        }
        .frame(width: 300, height: 300)

        Button("Animate In / Out") {
            showingSecondView.toggle()
        }
        .padding()
    }
}