How to kill the views of a navigation controller? - swift

i have some navigations controller
Navigation Controller 1
Navigation Controller 2
when the user log off (red box), should be back to screen log in (yellow box)
two navigation controllers are involved, the first one that is in the red box must die completely, just like that tab bar that is included.
this is the code of the close session button.
#IBAction func CerrarSesion(_ sender: Any)
{
do
{
try Auth.auth().signOut()
let vc = self.storyboard?.instantiateViewController(withIdentifier: "InicioSesionLogin")
//self.dismiss(animated: true, completion: nil)
self.present(vc!, animated: true, completion: self.borrarUserDefaults)
}
catch let error as NSError
{
print (error.localizedDescription)
}
}
this code returns me to the view of logging in without a issue, but the views that are ahead are still working, I know because there is a view that uses location functions of beacons, and leave a function that prints ("ranging").
In summary: I want to return to the view to log in (red box) and kill all the views that I have in front of me.
the only thing I could do, is to kill the first navigation controller (yellow box) but it returns me to a back view, where you see the gray box.

if the navigation controller is the rootVc , you can reset it's viewcontrollers property
self.navigationController.viewControllers = [VC]
or reset the rootVC
UIApplication.shared.keyWindow?.rootViewController = // VC

Related

Switch tab bar controller view controller after dismissing a modally presented view controller

In my project you can create a post from a modal view.
When the modal view is dismissed (user presses on save post) I want to switch the tab bar controller to the second tab (post feed screen).
This topic is similar to my problem. The only difference being this is presented from a modal view. I can't figure out how to implement it in my code (tab bar is nil)
Switch tab bar programmatically in Swift
I have added 3 images to make this issue clearer
code screenshot
console message
#objc func saveAction(sender: UIButton) {
print ("> save pressed")
print(presentingViewController?.tabBarController)
print(presentingViewController)
presentingViewController?.tabBarController?.selectedIndex = 1
dismiss(animated: true)
}
edit: sorry stack overflow doesn't allow me to add images yet
You can do this using delegate pattern. But if you prefer not to add a delegate for this, you can do as shown below;
You can switch the tabbar by changing the selectedIndex property of tabBarController
if let presenter = presentingViewController as? LibraryViewController {
presenter.tabBarController?.selectedIndex = 1
}
dismiss(animated: true)
If you are presenting the modal on navigation controller in tabbar, use:
if let tabBar = presentingViewController as? UITabBarController {
tabBar.selectedIndex = 1
}
dismiss(animated: true)

Back Button Takes Me To Wrong View Controller

I have a storyboard I'm working with that is setup as so:
Login Screen -> Tab Bar Controller - > Navigation Controller - > Screen 1 Segue to Screen 2 Segue to Screen 3.
The first time I login, everything works great. Screen 1 segues to screen 2, screen 2 segues to screen 3, you can then use the back button to go back to screen 2 and then screen 1. However, I have a "logout" function (code below, although I don't think this is relevant to my issue) and after I "logout", it takes me to the Login Screen (first screen in the sequence above). When I then login again, navigate to Screen 2 or Screen 3, pressing the back button from the segue takes me all the way back to the login screen as opposed to the prior Screen 1 or Screen 2.
#objc func logOut(){
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(homeView, animated: true)
homeView.navigationItem.hidesBackButton = true
}
I'm not sure that instantiating the LoginViewController again is the best practice, since you already have it in you navigation stack. I would recommend doing something like this:
#objc func logOut(){
self.navigationController?.popToRootViewControllerAnimated(true)
}
This will remove all the view controllers from the navigtion stack, and present you with a root view controller (LoginViewController)
Updated for anyone who is curious. The pushViewController option was the issue. If i just use "present", I no longer have this issue. Here is my updated logout code:
#objc func logOut(){
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.navigationController?.present(homeView, animated: true)
homeView.navigationItem.hidesBackButton = true
}

Segue To Navigation Controller in Tab Bar Controller

In my application, I have a default tab selected for my Tab Bar Controller. On the viewDidLoad() of the default tab, I have this:
if (NSUserDefaults.standardUserDefaults().stringForKey("defaultCode") == nil) {
//let navController:UINavigationController = UINavigationController()
//self.presentViewController(navController, animated: true, completion: nil)
self.tabBarController!.selectedIndex = 1
I thought calling selectedIndex would do what I was looking forever; however, it only changes the selected tab in the TabBar, but my view does not change. The area I commented out quickly shows the proper view, then goes to a black screen. The view I am trying to switch to is a Navigation Controller
selectedIndex always be the first and pop/push after selected

Warning: Attempt to present ViewController on ViewController which is already presenting ViewController

I have a view controller with a toolbar with 3 UIButtons that open a new view controller as a popover. I created the segues in Storyboard and selected "Present as Popover". The popovers work but when the user taps on another button while a popover is currently open, I get this error:
Warning: Attempt to present <Fingerpainter.OpacityViewController: 0x79095110> on <Fingerpainter.DrawingViewController: 0x7b278000> which is already presenting <Fingerpainter.BrushSizeViewController: 0x79573770>
Is there a way to like make sure all popovers are closed before opening a new one? Here's my prepareForSegue method in the main ViewController (containing the toolbar):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let identifier = segue.identifier ?? ""
let popoverPresentationController = segue.destinationViewController.popoverPresentationController
popoverPresentationController!.delegate = self
switch identifier {
case Storyboard.BrushSizeSegueIdentifier:
if let brushSizeViewController = popoverPresentationController?.presentedViewController as? BrushSizeViewController {
// set properties in brushSizeViewController
}
case Storyboard.OpacitySegueIdentifier:
if let opacityViewController = popoverPresentationController?.presentedViewController as? OpacityViewController {
//set properties in opacityViewController
}
case Storyboard.ColorSegueIdentity:
if let colorViewController = popoverPresentationController?.presentedViewController as? ColorViewController {
//set properties in colorViewController
}
default:
break
}
}
Is there a way to like make sure all popovers are closed before opening a new one
It's the other way around. It's your job to make sure that while the popover is present, a button that summons another popover is not tappable. You can do this by disabling the button, but more commonly, in order to coordinate the disabling of the button with the presence of the popover, it's done by adjusting the popover presentation controller's passthroughViews.
Unfortunately there's a massive and long-standing bug where even setting the passthroughViews to nil doesn't prevent toolbar buttons from being tappable. The workaround is to do it with a delay. A lot of my popover code adds this sort of thing:
if let pop = popoverPresentationController {
delay(0.1) {
pop.passthroughViews = nil
}
}
(where delay is described here: https://stackoverflow.com/a/24318861/341994).

What is the correct way to push a View Controller after a selection from a menu?

In my app I have a "home" screen from which I can present a hamburger menu (coded as a modal transition in Storyboard).
The menu is a UITableView where a user will select a row. The menu has a delegate method which calls the following function in the presenting screen (my "home" screen)
// Menu delegate method
func menuDidExit(menuVC:MenuVC) {
self.dismissViewControllerAnimated(true, completion: {
// After dismissal of menu, call the chosen option
if let selectedOption = menuVC.menuSelection {
self.performSegueWithIdentifier(menuVC.menuSelection?.rawValue, sender: self)
}
})
}
This function will both dismiss the menu view controller, and then (once that transition is complete), present a second view controller based upon the chosen menu option, using the "performSegueWithIdentifier" command.
The issue that I have is that while the menu dismiss works fine (the menu slides off screen gracefully) - there is no animation for the presentation of the next view controller - it simply appears on screen after the menu has been dismissed.
I can call the desired view controller via a button/segue, and all works well, however when it is part of the completion block above it fails to animate the transition. This leads me to believe that there is something fundamentally wrong with my approach - hence the question, what is the correct way to push a second view controller after handling the dismissal of the first.
Many thanks for any suggestions
I think you can use this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
self.dismiss(animated: true) {
//dismiss your menu here
}
}
Hope it can help you.
Please let me know if it not work.
Thanks