I am trying to pop a view controller VC1 from one navigation controller NC1 to another view controller VC2 in another navigation controller NC2.
I am using the VIPER architecture thus routing between different view controllers in different navigational hierarchies requires that I must switch to the root of the navigation controller I want to present.
So the issue here is that, after navigating to the view controller VC2 of the second navigation controller NC2, I want to go back to the first view controller VC1 in the first navigation controller NC1.
I have tried the following:
self.navigationController?.popViewController(animated: true)
navigationController?.dismiss(animated: true, completion: nil)
view.window?.rootViewController?.dismiss(animated: true, completion: nil)
self.navigationController?.popToRootViewController(animated: true)
but none of them worked for me. Any ideas?
Please Try this codes :
let viewControllers = self.navigationController?.viewControllers
for vc in viewControllers! {
if vc is VC1 {
self.navigationController?.popToViewController(vc as! VC1, animated: true)
}
}
Related
so I have a main view controller that the view controllers will present inFront of each other and I want when user click the button in the last view controller close all of the presented modally view controllers so I used this code But I didn't get the result
let destination = matchViewController()
let appDelegate:UIApplicationDelegate = UIApplication.shared.delegate!
let initialViewController = destination
let navigationController = UINavigationController(rootViewController: initialViewController)
appDelegate.window??.rootViewController = navigationController
appDelegate.window??.makeKeyAndVisible()
I want to use unwind segue to exit But there is another problem too
the last view controller will present many times in many different situations so I just to dismiss all presented modally view controllers in. this situation
I rather not using the navigationController But if I had to use it pleas tell me where exactly should I use that ?
Two options:
Dismiss all view controllers on the root view controller
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
Dismiss all viewControllers until it has a presentingController
func dismissAllControllers() {
guard let vc = self.presentingViewController else { return }
while (vc.presentingViewController != nil) {
vc.dismiss(animated: true, completion: nil)
}
}
As you can see i have an initial view controller with label in it "Initial ViewController". In this view controller i use below code to go to selected tab of Tab Bar Controller
let vc1 = sb.instantiateViewController(withIdentifier: "tabbar") as! UITabBarController
vc1.selectedIndex = selected
let controllers = [vc1]
navigationController!.setViewControllers(controllers, animated: true)
In my First View, there's a button "Present VC" which presents "Presented View".
let vc1 = sb.instantiateViewController(withIdentifier: "PresentedVC") as! PresentedVC
present(vc1, animated: true, completion: nil)
Now i want to go back from presented vc to root vc(Initial View Controller). I can do this with:
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "FirstNavigationController") as! FirstNavigationController
UIApplication.shared.keyWindow?.rootViewController = viewController
but this instantiate Initial View Controller every time and views remain in memory. How can i go back to Initial View Controller without instantiating it?
PS: FirstNavigationController is initial Navigation Controller.
At the very beginning you remove the initial view controller by doing this:
navigationController!.setViewControllers(controllers, animated: true)
All the previous viewControllers are gone now and replaced with the tabbar controller and its children.
If you want to go back to the initial controller, you will have to push the tabbar onto the stack instead of replacing everything with it. E.g.
navigationController?.pushViewController(tabbarVc, animated: true)
First when you did this
navigationController!.setViewControllers(controllers, animated: true)
the initial VC is deallocated (if it has not a strong references)
to keep it you can use push , pop instead of setViewControllers but note it may cause memory issues if you have heavy processing in your app as it will remain in navigationController stack
You are missing the concept between "Presenting" or "Setting" View Controller & "Navigating" the View Controller. You will get the answer, once you understood the concept. Here, it is..
When you are setting the ViewController, you are completely replacing the stack container to the new view controller. the way you did it here:
navigationController!.setViewControllers(controllers, animated: true)
In this case, STACK holds the addresses of the latest set/presented ViewControllers that means previous whole ViewController vanished.
On the other hand, if you are navigating to other view controller by pushing it. you can go back to previous controller by simple popping the ViewController address from stack. or to next ViewController by pushing
e.g:
self.navigationController?.pushViewController(tabbarVc, animated: true)
self.navigationController?.popViewController(animated: true)
Summary:
If you push TabBarController then, only you will get InitialVC.
Now, in your case, you are setting the ViewController and hence, you are not getting InivtialVC. Try Pushing tabbarVC This will work.
navigationController?.pushViewController(tabbarVc, animated: true)
I have three view controllers.
The first two view controllers use a navigation controller.
The third view controller is displayed modally.
The first view controller displays the second view controller using a segue in the storyboard, so there is no code involved here.
When the second view controller displays the third view controller modally and pops itself to the first view controller, using popToRootViewController, I get the error message:
Unbalanced calls to begin/end appearance transitions for SecondViewController
The code of the second view controller presenting the third view controller and popping to the first view controller is like this:
if let thirdViewController = self.storyboard?.instantiateViewController(withIdentifier: "ThirdViewControllerID") {
let navigationController = UINavigationController(rootViewController: thirdViewController)
self.navigationController?.present(navigationController, animated: true, completion: {
_ = self.navigationController?.popToRootViewController(animated: false)
})
The line of code causing the problem in the second view controller is:
_ = self.navigationController?.popToRootViewController(animated: false)
The source code showing the error is available here.
The solution is in the second view controller to call popToRootViewController outside the completion block of the navigationcontroller.present method like this:
if let thirdViewController = self.storyboard?.instantiateViewController(withIdentifier: "ThirdViewControllerID") {
let navigationController = UINavigationController(rootViewController: thirdViewController)
self.navigationController?.present(navigationController, animated: true, completion: nil)
_ = self.navigationController?.popToRootViewController(animated: false)
}
Notice the popToRootViewController is not anymore part of the UINavigationViewController.present(completion:) closure but is now outside and this solves the problem.
The source code with the fix is available here.
I want to segue to a new ViewController programatically but when I do my tabBar disappears.
if user == usernameStored && pass == passwordStored{
print("Good")
let vc = self.storyboard?.instantiateViewController(withIdentifier: "home")
self.present(vc!, animated: true, completion: nil)
}
From your code, this is not segue by programmatically. You actually present a viewController on top of whatever you have. Therefore the tabBarController is cover.
To use segue in code, it should be something like this. - homeSegueID is the identifier you give when you created the segue in storyboard.
performSegue(withIdentifier: "homeSegueID", sender: nil)
If you just want to do it programatically without segue, you could do this instead. (This assume your current ViewController is in a UINavigationController stack.
navigationController?.pushViewController(vc, animated: true)
Is the navigation controller wrapping your view controllers a tab bar controller, or did you just add the tab bar to your view controller? This is what you should be doing:
I have a view controller that shows the details of an object. On this view controller is an "edit" button which shows modally the edition view controller. When I try to dismiss the modally presented view (edit view controller) :
self.dismissViewControllerAnimated(true, completion: nil)
I get the following error and it's presenting my initial viewController instead :
Warning: Attempt to present ≤Deevent.MyEventsVC: 0x7f99b70160a0≥ on ≤Deevent.EventCreationVC: 0x7f99b7238690≥ whose view is not in the window hierarchy!
So what I've tried was to set the root view controller of my view to the view I wanted to go back and present it in the completition of my dismiss. It's working well, but my application is in a Tabbar Controller and now it's not in it anymore. Same for the navigation controller.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("MyEventsStoryboard") as! MyEventsVC
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.window?.rootViewController = vc
self.dismissViewControllerAnimated(false, completion: {
self.presentViewController(vc, animated: true, completion: nil)
})
Is there an other approach for presenting viewControllers after dismiss without leaving the Tabbar controller ?
Thanks
Since you are trying to present the new view controller by calling self.presentViewController after you dismiss self, you get the error. I can offer you a solution if you are using a navigation controller.