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:
- Creates new
MenuViewController
but never presents it; - Calls
dismiss
on view controller that was never presented; and - Calls
openSupport
from thisMenuViewController
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:
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)
}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
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
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
Changing Navigation Bar Color in Swift
Undefined Symbols for Architecture X86_64 on Xcode 6.1
Navigation Bar Rightbaritem Image-Button Bug iOS 11
Uicollectionview: Must Be Initialized with a Non-Nil Layout Parameter
Long Press Gesture on Uicollectionviewcell
App "Does Not Contain the Correct Beta Entitlement"
How to Pass Multiple Values with a Notification in Swift
Getting the Word Touched in a Uilabel/Uitextview
Dynamically Changing Font Size of Uilabel
How to Get an Iso 8601 Date on iOS
Ibeacon: Didrangebeacons Stops Getting Called, Must Reset Device for It to Work Again
Xcode Stuck at "Your Application Is Being Uploaded"
Swiftui Navigationview Navigationbartitle Layoutconstraints Issue
iOS App Freezes on Pushviewcontroller