skip to Main Content

I’m trying to animate in a view and make it appear as if it’s a sort of drawer opening from another view. This is all fine except if the first view is not opaque. It appears that you can see the animating view the moment it begins animating. Is there a way to clip this so it appears that the view is growing from the top of the bottom view?

Even without opacity this is an issue if where you’re animating in from isn’t a covered (demoed in second gif)

Animation example
Other animation example

Sample Code:

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)
      }
}

2

Answers


  1. Here a way for you:

     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)
            
        }
    
    }
    

    enter image description here

    Login or Signup to reply.
  2. It is possible to do by clipping exact container of ‘drawer’. Here is a demo of possible approach.

    Tested with Xcode 13.2 / iOS 15.2 (Simulator slow animation is ON for better demo)

    demo

    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()
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search