Dismiss and Present View Controller 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)

Present a view controller, dismiss it and present a different one in Swift

The error occurs because you are trying to present SecondController from FirstController after you have dismissed FirstController. This doesn't work:

self.dismiss(animated: true, completion: {
let vc = SecondController()

// 'self' refers to FirstController, but you have just dismissed
// FirstController! It's no longer in the view hierarchy!
self.present(vc, animated: true, completion: nil)
})

This problem is very similar to a question I answered yesterday.

Modified for your scenario, I would suggest this:

weak var pvc = self.presentingViewController

self.dismiss(animated: true, completion: {
let vc = SecondController()
pvc?.present(vc, 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
}
}

Dismiss and Present View Controller in Swift

You have to get the viewController which presented self (current ViewController). If that view controller is rootViewController, then you can use the code below, if not then query it based on your view controller hierarchy.

if let vc3 = self.storyboard?.instantiateViewController(withIdentifier: "vc3") as? ViewController3 {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController!.present(vc3, animated: true, completion: nil)
}

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

Disable the interactive dismissal of presented view controller

Option 1:

viewController.isModalInPresentation = true

Disabled interactive dismissal

(Disabled interactive .pageSheet dismissal acts like this.)

  • Since the iOS 13, UIViewController contains a new property called isModalInPresentation which must be set to true to prevent the interactive dismissal.
  • It basically ignores events outside the view controller's bounds. Bear that in mind if you are using not only the automatic style but also presentation styles like .popover etc.
  • This property is false by default.

From the official docs: If true, UIKit ignores events outside the view controller's bounds and prevents the interactive dismissal of the view controller while it is onscreen.



Option 2:

func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
return false
}
  • Since the iOS 13, UIAdaptivePresentationControllerDelegate contains a new method called presentationControllerShouldDismiss.
  • This method is called only if the presented view controller is not dismissed programmatically and its isModalInPresentation property is set to false.

Tip: Don't forget to assign presentationController's delegate. But be aware, it is known that even just accessing the presentationController can cause a memory leak.



Related Topics



Leave a reply



Submit