在 SwiftUI 中从侧面动画视图

Animate a view from the side in SwiftUI

说我有这样的看法:

struct CircleView: View {
    var body: some View {
        Circle()
    }
}

换一种说法,我有这样的看法:

var body: some View {
    GeometryReader { geo in
        ZStack {
            //some other views
                
            if someState == .showCircle {
                CircleView()
            }
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .edgesIgnoringSafeArea(.all)
        }
    }

someState 变为 .showCircle 时,我想让 CircleView 从屏幕右侧动画到左半球可见的位置,所以它在屏幕的中间,并调暗 ZStack 中的其余内容。设置这种动画的最佳方法是什么?

您可以在圆上使用 .transition() 修饰符,在 不是 圆的其他视图上使用 .blur() 修饰符,结合 .animation().

此外,在“非圆形”的视图上,您​​需要添加另一个修饰符:.allowsHitTesting(),以避免用户与模糊的视图进行交互。

请记住,当你触发someState时,你必须使用.withAnimation()闭包,否则圆圈不会滑入。

代码如下所示(我在 ZStack 上添加了一些内容只是为了提供示例)。为了方便起见,我还在示例中将 someState 变量设为布尔值,但在您的情况下,您只需检查枚举即可。

CircleView

struct CircleView: View {
    @Binding var someState: Bool
    var body: some View {
        Circle()
            .foregroundColor(.red)
            .overlay(Text("All is blur"))
            .onTapGesture {
                someState.toggle()
            }
        
        // These modifiers are necessary to animate the circle
            .transition(.move(edge: .trailing))
            .animation(.easeInOut, value: someState)
    }
}

另一种观点

    @State private var someState = false
    var body: some View {
        GeometryReader { geo in
            ZStack {
                VStack {
                    Button {
                        
                        // Without this closure, the circle does not slide in
                        withAnimation {
                            someState.toggle()
                        }
                    } label: {
                        Text("Show circle")
                    }
                    .padding()
                    
                    Spacer()
                    
                    Text("Bottom")
                        .padding()
                }
                
                // This modifier blurs the view that is not the circle
                .blur(radius: someState ? 5 : 0)
                
                // This modifier animates the blur effect
                .animation(.easeInOut, value: someState)
                
                // This is necessary to block the view from accepting user taps
                // when the circle is showing
                .allowsHitTesting(!someState)
                
                if someState {
                    CircleView(someState: $someState)

                        // Stop the circle half-way
                        .offset(x: UIScreen.main.bounds.width / 2, y: 0)
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .ignoresSafeArea()
        }
    }