Dismiss viewcontroller depends on presentation style (modal or push) - swift

I did FoodTracker programmatically and here is the code that doesn't work for me.
func cancel(){
let isPresentingInAddMealMode = presentingViewController is UINavigationController
if isPresentingInAddMealMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController {
owningNavigationController.popViewController(animated: true)
}
else {
fatalError("The MealViewController is not inside a navigation controller.")
}
}
Here is how it looks in the storyboard:
Actually it works modally when the plus button is pressed but it doesn't work with popviewcontroller. I pushed it in the navigation stack with didSelectRow in tableViewController.

Related

How to dismiss a navigation controller?

I'm presenting a navigation controller (detailview) with showDetailViewController(). I want to dismiss it when a button is pressed. How can I dismiss this viewcontroller?
Code I've attempted:
//detailviewcontroller
#objc
func cancel(_ sender: Any) {
print("cancelPressed")
//self.navigationController?.popViewController(animated: true)
//self.navigationController?.dismiss(animated: true, completion: nil)
//self.dismiss(animated: true, completion: nil)
//splitViewController?.dismiss(animated: true, completion: nil)
//splitViewController?.navigationController?.popToRootViewController(animated: true)
//splitViewController?.navigationController?.popViewController(animated: true)
//splitViewController?.navigationController?.dismiss(animated: true, completion: nil)
//navigationController?.popViewController(animated: true)
//navigationController?.dismiss(animated: true, completion: nil)
//self.navigationController?.popToRootViewController(animated: true)
//self.navigationController?.viewControllers.remove(at: 1)
//self.navigationController?.viewControllers.remove(at: 0) - this one removes to blank view
//self.presentingViewController?.dismiss(animated: true, completion: nil)
}
I've tried multiple stackoverflow solutions:
Dismissing a navigation view controller
How to dismiss a view controller in a navigation controller, without dismissing the whole stack
ios swift - dismiss root view controller of navigation controller
Can't dismiss View Controller that's embedded in a Navigation Controller
How to dismiss a navigation controller presented from another navigation controller in iOS 10 and below?
Can't dismiss navigation controller in Swift
Can't dismiss navigation controller in Swift
Dismissing View Controller Doesn't Work While Using Navigation Controller
Dismiss current navigation controller when clicked tab bar
How to dismiss a certain view controller
How I'm presenting the detailviewcontroller:
//masterviewcontroller
// delegation for passing data between controllers
weak var passDelegate: PlaceSelectionDelegate?
func insertNewObject(_ sender: Any) {
if let detailViewController = passDelegate as? DetailViewController {
if let detailNavigationController = detailViewController.navigationController {
detailViewController.delegate = self
splitViewController?.showDetailViewController(detailNavigationController, sender: nil)
}
}
}
Expected results:
dismiss detail viewController on button press.
Actual results:
No dismiss.
Ok, I've been looking for a way of dealing with this showDetailViewController for a while now and although I'm not sure this answers your question, this is how I did it:
I have two controllers, let's call it FirstController and SecondController. They both inherit from UIViewController. When I click a certain navbar button in the FirstController, I use showDetailViewController to show SecondController as a sheet moving upwards, like the "calendars" button in the Calendar app.
In FirstController I implemented 2 functions: one to show the SecondController and one to dismiss it. I call the dismiss action from within the SecondController. It looks like this:
// FirstController
#objc func showSecondView() {
let vc = SecondController()
vc.delegate = self
let nav = UINavigationController(rootViewController:vc)
self.showDetailViewController(nav, sender: self)
}
func closeSecondView() {
self.dismiss(animated: true)
}
As you can see I wrapped my SecondController in a UINavigationController, so it shows the navbar with title and all. This is how the code looks like:
// SecondController
weak var delegate: FirstController!
override func viewDidLoad() {
super.viewDidLoad()
title = "Second"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closeMe))
}
#objc func closeMe() {
delegate.closeSecondView()
}
Notice how I made a property for the FirstController and I use it to dismiss the SecondController. When I presented SecondController I assigned self as delegate, which means I can now call the closeSecondView method from within it.
I am not sure whether it works out of the box with split-view. The documentation seems to suggest that whenever you use it, if the screen is big enough, it behaves differently because UISplitViewController overrides it. It also says there that all view controllers have this method.
Take a look: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621432-showdetailviewcontroller
Give it a go, I hope this helps.
This should work if you simply want to dismiss the navigation controller
navigationController?.dismiss(animated: true, completion: nil)

Present a custom UIAlert from a UINavigationController stack view

I am having trouble presenting a CustomAlert, subclass of UIViewController, from a UINavigationController stack view. To be more descriptive, I have a UITabBarController that wraps a UINavigationController. navController, has a rootView and a another pushed to the stack view, leafView of type UITableViewController. From leafView, I would like to present an alertView of type UIViewController which is transparent for the most part and with a opaque view inside. The problem is that when presenting, the background of this alertView is no longer transparent, but black, and upon dismissal it does not return to the leaf controller, but to the rootView. I think I am not presenting my alert from the correct view. How should I fix this?
searchController.present(friendAlert, animated: false, completion: nil)
For alerts, it is much easier to present them from the "top view controller" instead of searching for the responsible controller in your hierarchy. A genius solution to find the controller is shown in this answer:
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
}
}
You can then show your alert from anywhere and after dismissing your alert you will get right back to the current view controller:
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in }))
UIApplication.topViewController()?.present(alert, animated: true, completion: nil)
}

Swift - How to remove a viewcontroller from NavigationController?

I've got 4 ViewControllers attached to a NavigationController. The order of them is 1->2->3->4. When the user presses the back button on 4, I'd like them to be redirected to 2 instead of 3. At the same time, I'd also like the user to be directed back to 2 when the back button is pressed on 3. Is this possible? Thanks in advance.
Of course you can do this. Simply create the left bar button on 4th ViewController. and on that button action pop to 2nd viewcontroller
if let viewcontroller = self.navigationController?.viewControllers[1] where viewcontroller.isKindOfClass(YourController) {
self.navigationController?.popToViewController(viewcontroller, animated: false) }
if let vc = self.viewControllerWithClass(YourVC.self) {
self.popToViewController(vc, animated: true)
}
extension UINavigationController {
func viewControllerWithClass(_ aClass: AnyClass) -> UIViewController? {
for vc in self.viewControllers {
if vc.isMember(of: aClass) {
return vc
}
}
return nil
}
}
You can check for the controller in navigation stack.
let controllers = navigationController!.viewControllers.reverse()
for controller in controllers
{
if controller.isKindOfClass(YourController)
{
self.navigationController?.popToViewController(controller, animated: true)
return
}
}

Can one uitableviewcell segue 2 view?

i wanted to ask, if i got one uitableviewcell, i need to do checking to meet the condition then only perform to different segue to different view, how to do it? Example like:
if subCat[indexPath.row] == ""{
performSegueWithIdentifier("A", sender: self)
}else{
performSegueWithIdentifier("B", sender: self)
}
How to do it? And how should i connect it with segue at the storyboard?
Even you can do by giving storyboard identifier "A" to viewController 1 and "B" to viewController 2 and push you controller if using navigation otherwise simply present the viewcontroller.
if subCat[indexPath.row] == "" {
let viewController1 = storyboard.instantiateViewControllerWithIdentifier("A") as! ViewController1
// if navigation controller used.
self.navigationController?.pushViewController(viewController1, animated: true)
// if navigation controller not used.
// self.presentViewController(viewController1, animated: true, completion: nil)
}else{
let viewController2 = storyboard.instantiateViewControllerWithIdentifier("B") as! ViewController2
// if navigation controller used.
self.navigationController?.pushViewController(viewController2, animated: true)
// if navigation controller not used.
// self.presentViewController(viewController2, animated: true, completion: nil)
}

Back when current view controller is not presented by segue. Swift

I am trying to present VC2 from VC1 without using segue. It works. Then, I tried to use self.navigationController?.popViewControllerAnimated(true) to back but it does not work. I am wondering what is the code I should use to back from VC2 to VC1. Below code is in appDelegate.
AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let VC2 = storyboard.instantiateViewControllerWithIdentifier("VC2") as! VC2
let navController = UINavigationController(rootViewController: VC2)
self.topViewController()!.presentViewController(navController, animated: false, completion: nil)
func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let MMDrawers = base as? MMDrawerController {
for MMDrawer in MMDrawers.childViewControllers {
return topViewController(MMDrawer)
}
}
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
VC2
#IBAction func backButtonTapped(sender: AnyObject) {
print(self.navigationController?.viewControllers) // print([<MyAppName.VC2: 0x12f147200>])
self.navigationController?.popViewControllerAnimated(true)
}
Here is the Flow of your navigation :
Current Screen - Presenting A New Screen (Which itself embed within a navigation controller with vc2) so Popviewcontroller won't work .
If you present any viewcontroller then popviewcontroller wont work rather use dismissviewcontroller to come out previous screen .
Use This :
self.dismissViewControllerAnimated(true, completion: {})
Solved!
Thanks.