SwiftUI how to have next and back animations?

Viewed 1563

I made sample app with 3 pages and "Next", "Back" buttons.

enter image description here

When I click "Next" animation is perfect: the old content goes to the left and new content comes from the right.

However, I'm struggling to make "Back" animation to work nice simultaneously.

Goal: when clicking "Back" old content should go to the right and new content should come from the left. (animation for "Back" should be reverse to animation for "Next")

Any idea how to accomplish this?

Below is perfectly working transition for "Next"

struct ContentView: View {
    @State var page: Int = 0
    
    var body: some View {
        
        VStack {
            HStack {
                Button(action: { withAnimation() { self.page = self.page - 1 } }) {
                    Text("Back")
                }
                
                Spacer()
                
                Button(action: { withAnimation() { self.page = self.page + 1 }}) {
                    Text("Next")
                }
            }
            Spacer()
            
            if page == 0 {
                PageView(name: "First page", color: .brown)
                    .transition(AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
            } else if page == 1 {
                PageView(name: "Second page", color: .systemGreen)
                    .transition(AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
            } else if page == 2 {
                PageView(name: "Third page", color: .systemBlue)
                    .transition(AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
            }
        }
    }
}

struct PageView: View {
    var name: String
    var color: UIColor
    var body: some View {
        HStack {
            Spacer()
            Text(name)
            Spacer()
        }
        .padding()
        .padding(.vertical, 50)
        .background(Color(color))
    }
}
1 Answers

You need to reverse transition when navigating back.

Here is possible approach (also made transition in one place and corrected animation to work everywhere, including Preview).

Tested with Xcode 12 / iOS 14.

demo

struct ContentView: View {
    @State var page: Int = 0

    @State private var isBack = false   // << reverse flag (not animatable)
    var body: some View {

        VStack {
            HStack {
                Button(action: {
                    self.isBack = true
                    self.page = self.page - 1
                }) {
                    Text("Back")
                }

                Spacer()

                Button(action: {
                    self.isBack = false
                    self.page = self.page + 1
                }) {
                    Text("Next")
                }
            }
            Spacer()

            Group {
                if page == 0 {
                    PageView(name: "First page", color: .brown)
                } else if page == 1 {
                    PageView(name: "Second page", color: .systemGreen)
                } else if page == 2 {
                    PageView(name: "Third page", color: .systemBlue)
                }
            }.transition(AnyTransition.asymmetric(
                insertion:.move(edge: isBack ? .leading : .trailing),
                removal: .move(edge: isBack ? .trailing : .leading))
            )
            .animation(.default, value: self.page)   // << animate here by value
        }
    }
}
Related