1

I am trying to build an overlay of "popover" views and animate transitioning in and out. Transitioning out works, but transitioning in doesn't -- as a popover view is added it just suddenly appears (wrong), but when a popover view is removed it slides out to the right (correct). How can I make the popover slide in (from right) when it's added to the view hierarchy in this code?

Fully functional code in iOS 14.

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

1 Answer 1

1

The solution is to add instead animation to container. Tested with Xcode 12 / iOS 14.

demo

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))
        )
    }
    
}
Sign up to request clarification or add additional context in comments.

1 Comment

Works great, but why does it work now - why did it work for transition out but not in?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.