Best practice with back swipe gesture #30
Replies: 2 comments
-
Sad to say I'm unaware of a method or trick to implement something that fully mimicks iOS' native swipe-to-go-back UX, or to use SwiftUI's NavigationView. The big challenge here is that SwiftUIRouter is unable to render both the current and previous path simultaneously. And it's unaware of the states of the NavigationView/NavigationLinks. For one app I did implement something that's somewhere between the native UX and your example: When swiping from the left side, the view moves along with the user's finger. Except instead of revealing the previous view - like in iOS - it just reveals a black background. When the user's finger reaches a certain point it commits the navigation. (However, rather than doing It's not 100% the same as one might expect from an iOS app - but considering the app deviates a lot from iOS' default UI design anyway, it does the job. 😛 At one point I did come up with the idea to potentially use two I'd love to see more concepts and implementations from other developers. If anyone has better ideas, please share them! 😄 |
Beta Was this translation helpful? Give feedback.
-
Here's a crude implementation one may draw some inspiration from (or just use as-is instead): struct SwipeableBack: ViewModifier {
enum Action {
case back
case up
}
@EnvironmentObject private var navigator: Navigator
@State private var dragOffset: CGFloat = 0
let action: Action
let threshold: CGFloat
private var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
if (action == .back && navigator.canGoBack && value.startLocation.x < 20)
|| (action == .up && navigator.path != "/")
{
dragOffset = value.translation.width * 0.2
}
}
.onEnded { value in
if dragOffset > threshold {
if action == .back {
navigator.goBack()
}
else if action == .up {
navigator.navigate("..")
}
}
dragOffset = 0
}
}
func body(content: Content) -> some View {
content
.offset(x: max(0, dragOffset))
.clipped()
.background(Color.black)
.gesture(dragGesture)
}
}
extension View {
func swipeableBack(action: SwipeableBack.Action = .back, threshold: CGFloat = 40) -> some View {
modifier(SwipeableBack(action: action, threshold: threshold))
}
} The view modifier can be applied to either a Route(path: "music/*") {
MusicScreen()
.swipeableBack(action: .up)
}
Route(path: "videos/*") {
ThirdScreen()
}
.swipeableBack() The effect is simple, but effective. With the above code it simply moves the view along with the pointer when dragging from the left edge, revealing a black background behind the view. But just as easily you could implement your own visual effect. (Like an arrow appearing from the screen's edge like in Chrome). When the pointer reaches a certain threshold and is released, a navigation action is committed. Combine this with the animations from the Animating routes doc and you've got an implmentation that, while not resembling 1:1 with the native iOS effect, is still satisfying enough to use from a UI/UX perspective. It comes with the bonus feature of choosing the navigation behavior when the view is dragged beyond a certain threshold. Hope this inspires you or anyone else coming across this question! 😄 |
Beta Was this translation helpful? Give feedback.
-
Obviously, without Animations, SwiftUIRouter does not provide native-like back swipes in the navigation hierarchy. Do you know any good way to replicate those edge pan swipes to feel native?
In iOS, swipe is a really important feature and ignoring it is not really an option. That being said using path-based routing offers great benefits over the traditional routing model in SwiftUI. Combining both would be ideal.
Currently, I am using a somewhat hacky solution which is really not nice at all compared with the native feel of back swipes:
as well as the animation
ViewModifier
as in your example in the docs.This leads to a fake swipe where the animation transition to the last View happens once you have done a swipe far enough starting from the left edge.
This is obviously not a real swipe, which means you can't stop at some point or swipe (drag) back and forth a little bit without releasing your finger. It just starts the animation at some points and that's it.
If you don't look closely that's enough. But it's certainly not the best ever user experience. Is there a way to combine the real
NavigationView
back swipes with this library at all? I imagine replicating it from scratch is too much work for too little outcome. Can we somehow reuse/abuseNavigationView
exactly for that?Beta Was this translation helpful? Give feedback.
All reactions