0
votes

SwiftUI Animate View Removal Not Working

I'm struggling with the animation modifiers on SwiftUI Views.

I have a mapping function in SwiftUI with MKMapView ViewRepresentable. I have an annotation with two controls. The code for animation listed here and the ComboDetailMapView below does indeed animate correctly for presentation of the subview - but nothing I have tried animates the view as it is removed.

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {

    if control == view.leftCalloutAccessoryView {
        //this animates in - but does NOT animate out - required here for ANY animation
        withAnimation(.easeInOut(duration: 1.0)) {
            parent.userDefaultsManager.showAddRemoveWaypointsView.toggle()
        }

    } else if control == view.rightCalloutAccessoryView {
        //this animates in - but does NOT animate out - required in this place
        withAnimation(.easeInOut(duration: 1.0)) {
            parent.userDefaultsManager.displayAddressView.toggle()
    }
    //...bunch more stuff
}

The view that I am animating is a simple white background image with opacity variation and an alert style subview.

struct DisplayAddressView: View {
    @ObservedObject var userDefaultsManager: UserDefaultsManager
    @Environment(\.presentationMode) var presentationMode

    var body: some View {

        ZStack {
            GeometryReader { geo in
                VStack {
                Image("PlainMask")
                    .resizable()
                    .frame(width: geo.size.width, height: geo.size.height + 20, alignment: .center)
                    .opacity(0.9)
                }//vstack
            }//geo reader

            RoundedRectangle(cornerRadius: 20)
                .fill(Color.blue).opacity(0.3)
                .frame(width: 300, height: 200)
            VStack {
                Button(action: {
                    //animation command does not work here
                    //withAnimation(.easeInOut(duration: 1.0)) {
                    self.userDefaultsManager.displayAddressView.toggle()
                }) {
                    VStack {
                        Text("Approximate Address")
                            //.font(.headline)
                            .foregroundColor(.primary)
                            .font(.system(size: 20) )
                            .multilineTextAlignment(.center)
                        Divider()
                        Text(self.userDefaultsManager.approximateAddress)
                            //.font(.headline)
                            .foregroundColor(.red)
                            .font(.system(size: 20) )
                            .multilineTextAlignment(.center)
                    }
                }
                .padding(.bottom)
                Divider()
                Button(action: {
                    //animation command does not work here
                    //withAnimation(.easeInOut(duration: 1.0)) {}
                    self.userDefaultsManager.displayAddressView.toggle()
                }) {

                    Text("Dismiss")
                        //.font(.headline)
                        .foregroundColor(.primary)
                        .font(.system(size: 20) )
                }
            }
        }//Zstack
    }//body
}

And this is the view that launches the DisplayAddressView view:

struct ComboDetailMapView: View {

    @ObservedObject var userDefaultsManager: UserDefaultsManager
    var aTrip: Trip?

    var body: some View {
        ZStack {
            VStack {
                Text(aTrip?.name ?? "Unknown Map Name")
                    .padding(.top, -50)
                    .padding(.bottom, -20)
                DetailMapView(userDefaultsManager: userDefaultsManager, aTrip: aTrip)
                    .padding(.top, -20)
                Text(self.userDefaultsManager.tripTimeAndDistance)
                    .multilineTextAlignment(.center)
            }//vstack

            if userDefaultsManager.displayAddressView {
                DisplayAddressView(userDefaultsManager: userDefaultsManager)
                //this works to add slide to the other animation
                .transition(AnyTransition.opacity.combined(with: .slide))
                //this does not work for dismissal
                //.transition(.asymmetric(insertion: AnyTransition.opacity.combined(with: .slide), removal: .scale))
            }
        }//zstack
    }//body
} 

enter image description here

I've also tried the ideas of this SO 59064231 .No joy.

I confess the procedure to animate views since you can no longer attach the modifier directly to the view is still confusing me. Any guidance would be appreciated. Xcode 11.3.1 (11C504)

1
basically it is easier if there is an copyable and reproducable example to work on ...so i would suggest to break down to a reproducable example which people would like to help onChris

1 Answers

0
votes

It turns out that if you step back and think about the functionality that I require here, I can simply use a ternary to control the position of the view. No if statement needed to compare the binding, no complex .transition coordination. As a bonus, opacity can be animated too if desired.

DisplayAddressView(userDefaultsManager: userDefaultsManager)
    .offset(x: userDefaultsManager.showAddressView ? 0 : 500)
    .animation(.easeInOut(duration: 1.0))

Slick.