How to Implement "Drag Right to Dismiss" a View Controller That's in a Navigation Stack

How can I implement drag right to dismiss a View Controller that's in a navigation stack?

Sample Image

Made a demo project in Github
https://github.com/rishi420/SwipeRightToPopController

I've used UIViewControllerAnimatedTransitioning protocol

From the doc:

// This is used for percent driven interactive transitions, as well as for container controllers ...

Added a UIPanGestureRecognizer to the controller's view. This is the action of the gesture:

func handlePanGesture(panGesture: UIPanGestureRecognizer) {

let percent = max(panGesture.translationInView(view).x, 0) / view.frame.width

switch panGesture.state {

case .Began:
navigationController?.delegate = self
navigationController?.popViewControllerAnimated(true)

case .Changed:
percentDrivenInteractiveTransition.updateInteractiveTransition(percent)

case .Ended:
let velocity = panGesture.velocityInView(view).x

// Continue if drag more than 50% of screen width or velocity is higher than 1000
if percent > 0.5 || velocity > 1000 {
percentDrivenInteractiveTransition.finishInteractiveTransition()
} else {
percentDrivenInteractiveTransition.cancelInteractiveTransition()
}

case .Cancelled, .Failed:
percentDrivenInteractiveTransition.cancelInteractiveTransition()

default:
break
}
}

Steps:

  1. Calculate the percentage of drag on the view
  2. .Begin: Specify which segue to perform and assign UINavigationController delegate. delegate will be needed for InteractiveTransitioning
  3. .Changed: UpdateInteractiveTransition with percentage
  4. .Ended: Continue remaining transitioning if drag 50% or more or higher velocity else cancel
  5. .Cancelled, .Failed: cancel transitioning


References:

  1. UIPercentDrivenInteractiveTransition
  2. https://github.com/visnup/swipe-left
  3. https://github.com/robertmryan/ScreenEdgeGestureNavigationController
  4. https://github.com/groomsy/custom-navigation-animation-transition-demo

Drag down to dismiss a ViewController

These view controller transitions are, sigh, unfortunately very complicated to do but there are lots of good tutorials online about them. You are correct in putting the subview into its own view controller. There are several other moving parts required. Fully detailing them would be enough material to write a book on the topic, but the high level overview is this:

Chapter 1: The Container

Think of the view controller with the map view as view controller A. Think of the slide-up/down view as view controller B. We want to transition seamlessly from A to B (and B to A). Cocoa provides an API for doing this, but first you'll need a container view controller that contains both A and B as a child view controller. This could be a navigation controller or your own custom container controller.

Chapter 2: Noninteractive Custom Container View Controller Transition

Once you have the container view controller, code it up so that you can go from A to B and back by pressing a button. (Throw a debug button somewhere into your app.) So you tap a button and you go from A to B and then you tap it again to go from B to A. To do this, there are the noninteractive custom container view controller transition APIs (it's a mouthful). This will require implementing an animation controller and a transitioning delegate, as detailed in that excellent objc.io tutorial and its corresponding GitHub repository.

Chapter 3: Interactive Custom Container View Controller Transition

But, of course, you want users to swipe up and down to trigger the transition. This will take you into the world of interactive blah blah blah APIs, where you implement an interaction controller and connect it to a pan gesture recognizer. The corresponding GitHub repository to that blog post actually uses a pan gesture like you would. This is particularly finicky work, as you would want the transition to be canceled should the user decide to stop panning midway through, but all that is detailed in the code and article.

Chapter 4: View Sharing

I'm actually not sure what this is called. The problem here is that you want the Google Maps / Spotify look and feel where a part of view controller B's view actually is visible while you're in view controller A. And the pan gesture recognizer is attached to B's view so that when you slide it up, B's view gets bigger and bigger until the interactive animation finishes and you've transitioned from A to B. Is there a good tutorial on this? I haven't found one, but it's becoming more and more common as an iOS UX idiom.

I myself have a small demo project illustrating how to do this. What I do is I have a navigation controller as my container view controller. The navigation controller's root view controller is my home view controller (view controller A). In view controller, I have view controller B as a child. So that's navigation controller owns A owns B.

When the user taps on B, I push B onto the navigation stack, thus reparenting it. At that point, my noninteractive animation controller kicks in as a custom navigation controller push animation. And so it looks like B is smoothly transmogrifying from its small bounds to its larger bounds. (I recommend running the xcodeproj to see the effect as I have done a terrible job of describing it.)

This technique would work just as well for an interactive animation. As to whether it's the best technique, I think that's up for debate — there are lots of ways of doing what you see in the Google Maps and Spotify apps. Apple could certainly do a lot better job of documenting the APIs for complex, interactive animations.

I would caution you to avoid using Auto Layout until you have your animations tweaked and ready, as it doesn't really interact well with your animation controllers. Good ol' frame rectangle math is best here until you find your bearings. Auto Layout is magic and, as you might be able to tell from how exhaustingly long this answer is, there's already enough magic to go around.

See Also

  • Session 803 from WWDC 2015: Designing with Animation

  • Session 214 from WWDC 2014: View Controller Advancements

  • Session 218 from WWDC 2013: Custom Transitions Using View Controllers

You can click on the purple Apple logo in the top right of asciiwwdc.com pages to watch the videos of the talks or grab the PDFs from the Resources tabs. Some of the best documentation for iOS is locked up in these WWDC talks.

Swipe Right to Dismiss ViewControlelr without using Storyboard Swift

The way to dismiss a view controller is to call a dismiss method on the presenting view controller. If you presented your child controller from a parent by calling self.present(_:animated:) then you can call self.dismiss(animated:). If you used a navigationController and called navigationController.push(_:animated:) then you need to tell the navigation controller to dismiss with navigationController.popViewController(animated:)
Method names might not be exact, but you should be able to figure it our with autocomplete.

How to dismiss view controller opened with present?

Simple call

self.dismiss(animated: true, completion: nil)

Swipe horizontally to dismiss a view controller in swift

If I understand correctly your question, in viewDidLoad add this:

override func viewDidLoad() {
super.viewDidLoad()
...
setupGestureRecognizers()
}

Then:

private func setupGestureRecognizers() {
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(self.swipeAction(swipe:)))
leftSwipe.direction = UISwipeGestureRecognizer.Direction.left
view.addGestureRecognizer(leftSwipe)
}

And finally target-action:

@objc func swipeAction(swipe: UISwipeGestureRecognizer) {
self.dismiss(animated: true)
}

Update:
If you want custom dismiss animation, for example left or right, try this code inside swipeAction:

@objc func swipeAction(swipe: UISwipeGestureRecognizer) {
let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
self.view.window!.layer.add(transition, forKey: nil)
self.dismiss(animated: false, completion: nil)
}

Swift ios: drag view controller onto screen

To do this you will need to use a UIPageViewController and set its transition style to scroll and navigation to vertical. There are lot's of great tutorials on how to use a UIPageViewController so just look online.



Related Topics



Leave a reply



Submit