How to Dismiss Viewcontroller in Swift

How to dismiss ViewController in Swift?

From you image it seems like you presented the ViewController using push

The dismissViewControllerAnimated is used to close ViewControllers that presented using modal

Swift 2

navigationController.popViewControllerAnimated(true)

Swift 4

navigationController?.popViewController(animated: true)

dismiss(animated: true, completion: nil)

Swift: How to dismiss a ViewController programmatically?

Consider

@IBAction func buttonSupport(_ sender: UIButton) {
let menuView = MenuViewController() // (1)
menuView.dismiss(animated: true, completion: nil) // (2)
openSupport() // (3)
print("Tap on Support")
}

This:

  1. Creates new MenuViewController but never presents it;
  2. Calls dismiss on view controller that was never presented; and
  3. Calls openSupport from this MenuViewController instance (which was never dismissed).

Bottom line, you want to let the main view controller that presented the menu do the presenting. So, the menu view controller should:

  1. Define a protocol for it to inform the presenting view controller to transition to the next scene:

    protocol MenuViewControllerDelegate: class {
    func menu(_ menu: MenuViewController, present viewController: UIViewController)
    }
  2. And then the menu view controller can, when it’s done dismissing, tell its delegate what it should present:

    class MenuViewController: UIViewController {
    weak var delegate: MenuViewControllerDelegate?

    @IBAction func didTapSupport(_ sender: Any) {
    dismiss(animated: true) {
    guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "support") else { return }
    self.delegate?.menu(self, present: controller)
    }
    }

    @IBAction func didTapCancel(_ sender: Any) {
    dismiss(animated: true)
    }
    }

Then the main view controller needs to

  1. Make sure to set the delegate of the menu view controller:

    class ViewController: UIViewController {
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? MenuViewController {
    destination.delegate = self
    }
    }
    }

    and

  2. Make sure to present the view controller that the menu controller asked it to:

    extension ViewController: MenuViewControllerDelegate {
    func menu(_ menu: MenuViewController, present viewController: UIViewController) {
    present(viewController, animated: true)
    }
    }

There are lots of different ways of achieving this, so don’t get lost in the details here. But the idea is to have some system by which the menu view controller can request whomever is to present the support view to do so, not try to do it itself.

How to correctly dismiss previous view controller

Think of it this way: A view controller can't exist on an island. It has to be presented on top of something.

That means when you present one VC on top of another, the presenting view controller is the "foundation" for the new one you just presented.

If you don't want to present VCs on top of each other, you have a couple of options:

1) Use a navigation controller. This is probably the best approach. You can present or push any view controller. If you decide to push, you can remove the old one from the navigation stack, or you can keep it there so the user can go back. There are lots of ways to use a navigation controller, and it's easily the most flexible way to navigate between controllers.

2) Use a tab bar controller. This works best if you have just a few different view controllers in your app, but it's good for certain use cases.

3) Do exactly what you said in your post (use the root view controller to present/dismiss all other VCs). As I said, you can't present a view controller out of thin air-- there always has to be something behind it. Unless there's a ton of stuff going on in your root VC, this shouldn't cause any memory issues. This approach should be fine unless you're very particular about the animations between your view controllers.

In general, I wouldn't worry too much about memory usage until it becomes a problem. It should be fine to present view controllers on top of each other for 99% of normal use cases.

How to dismiss previous Viewcontroller after presenting new Viewcontroller using Swift?

Try this in VC2's close button action

var vc = self.navigationController?.presentingViewController
while vc?.presentingViewController != nil {
vc = vc?.presentingViewController
}
vc?.dismiss(animated: true, completion: nil)

Swift: Dismissing all view controllers and then presenting a view controller

You can dismiss all viewcontrollers with below code block. In the completion block you can get the topViewController and you can present new viewController over topViewController. I also wrote down an extension for get the topViewController on the window.

UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: true, completion: { [weak self] in
// Get Top Controller With Extension
let topController = UIApplication.topViewController()
// Pressent New Controller over top controller
let congratsPopup = K.mainStoryBoard.instantiateViewController(withIdentifier: "congratsController") as! CongratsController
topController?.present(congratsPopup, animated: true, completion: nil)
})

Get Top View Controller Extension

extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
}

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)
}

Dismiss completly a view controller in middle

Found a way. I'm not sure if I'm correct or wrong, but here is my thought: View controller must be dismissed the way it's presented before. For example if you use present(viewController, animated, completion) to present, you should use dismiss(animated, completion) to dismiss. The problem, in my case, is if you dismiss a view controller, all child view controllers will be gone too.

The way I resolved my issue is using another way to "show" the target view controller:
parentVC.view.addSubview(childVC.view)
parentVC.addChild(childVC)
childVC.didMove(toParent: parentVC)

After adding some view controllers, I got a tree parentVC -> child1 -> child2 -> child3... If I want to dismiss child2, the code in my question worked:
child2.willMove(toParent: nil)
child2.view.removeFromSuperview()
child2.removeFromParent()

There is no UITransitionView blocking user actions, but it got no animation. For my this is good enough.

Dismiss a modal ViewController after X seconds

You can do it using DisaptchQueue:

    DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
// Do whatever you want
theViewControllerToDismiss.dismiss(animated:true, completion: nil)
}


Related Topics



Leave a reply



Submit