It doesn't come for free, but you can implement this kind of functionality quite easily by saving the last navigation target and handling drag gestures. Like this:
struct ContentView: View {
@State private var navPath = [Int]()
@State private var previousTarget = -1
private func dragNavigate(back: Bool) -> some Gesture {
DragGesture()
.onChanged() { value in
let translation = value.translation
if abs(translation.width) > abs(translation.height) &&
back == (translation.width > 0) {
if back && navPath.count > 0 {
navPath.removeLast()
} else if !back && previousTarget >= 0 && previousTarget != navPath.last {
navPath.append(previousTarget)
}
}
}
}
private func targetView(index: Int) -> some View {
Text("View\(index + 1)")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
.gesture(dragNavigate(back: true))
}
var body: some View {
NavigationStack(path: $navPath) {
VStack(spacing: 50) {
NavigationLink("View1", value: 0)
NavigationLink("View2", value: 1)
NavigationLink("View3", value: 2)
}
.navigationDestination(for: Int.self) { index in
switch index {
case 1: targetView(index: 1)
case 2: targetView(index: 2)
default: targetView(index: 0)
}
}
.onChange(of: navPath) { newPath in
if let target = newPath.last {
previousTarget = target
}
}
}
.gesture(dragNavigate(back: false))
}
}
If the navigation hierarchy extends to more than one level then you might need to save more than one previous target, perhaps as a shadow stack.