I'm having trouble creating transitions that properly emulate iOS navigation transitions. Different transitions should play depending on whether the user is navigating "forward" or "backward", but it seems like the view inside my switch statement isn't getting the most recent version of the state variable being updated. This results in strange behavior - after "going" forward, backward, and forward again, the view that's being removed isn't reading the newest value of my boolean variable and plays the old transition instead.
I understand why the problem is occurring, but it's extremely frustrating as I've spent the last two hours trying to fix this issue with no success. I've attached code and a GIF below. Thank you!
ContentView.swift
import SwiftUI
enum NavigationViewType {
case firstView, secondView, thirdView
}
struct ContentView: View {
@State private var currentView: NavigationViewType = .firstView
@State private var isReverse = false
var body: some View {
VStack {
switch currentView {
case .firstView:
FirstView(nextView: goToNextView, goBack: goBack, isReverse: $isReverse)
.transition(isReverse ? .reverseSlide : .slide)
case .secondView:
SecondView(nextView: goToNextView, goBack: goBack, isReverse: $isReverse)
.transition(isReverse ? .reverseSlide : .slide)
case .thirdView:
ThirdView(nextView: goToNextView, goBack: goBack, isReverse: $isReverse)
.transition(isReverse ? .reverseSlide : .slide)
}
}
.animation(.default)
}
func goToNextView() {
withAnimation {
isReverse = false
switch currentView {
case .firstView:
currentView = .secondView
case .secondView:
currentView = .thirdView
case .thirdView:
currentView = .firstView
}
}
}
func goBack() {
withAnimation {
isReverse = true
switch currentView {
case .firstView:
currentView = .thirdView
case .secondView:
currentView = .firstView
case .thirdView:
currentView = .secondView
}
}
}
}
struct FirstView: View {
var nextView: () -> Void
var goBack: () -> Void
@Binding var isReverse: Bool
var body: some View {
VStack {
Text("First View")
Button("Next") {
nextView()
}
Button("Back") {
isReverse = true
goBack()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)
}
}
struct SecondView: View {
var nextView: () -> Void
var goBack: () -> Void
@Binding var isReverse: Bool
var body: some View {
VStack {
Text("Second View")
Button("Next") {
nextView()
}
Button("Back") {
isReverse = true
goBack()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
}
}
struct ThirdView: View {
var nextView: () -> Void
var goBack: () -> Void
@Binding var isReverse: Bool
var body: some View {
VStack {
Text("Third View")
Button("Next") {
nextView()
}
Button("Back") {
isReverse = true
goBack()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow)
}
}
extension AnyTransition {
static var slide: AnyTransition {
let transition = AnyTransition.asymmetric(
insertion: AnyTransition.move(edge: .trailing),
removal: AnyTransition.move(edge: .leading)
)
return transition
}
static var reverseSlide: AnyTransition {
let transition = AnyTransition.asymmetric(
insertion: AnyTransition.move(edge: .leading),
removal: AnyTransition.move(edge: .trailing)
)
return transition
}
}