Swift: rootViewController never has its view[Will/Did]Disappear methods called - swift

So I have a normal UINavigationController with its rootViewController set in the storyboard. Whenever I replace the navigation controller (don't ask why, but certain transitions in my app require the entire stack to be replaced with the new controller) using
var newController: UIViewController!
// newController is initiated at some point
...
if let navigationController = self.navigationController {
navigationController.setViewControllers([newController])
}
And I can visually see the transition occur, but neither the viewWillDisappear or viewDidDisappear methods are called. When I subsequently replace the navigation controller stack they're called as expected. Is there something special about the root the prevents them from being called?

Basically Navigation controller works as a stack of controllers where child controllers go on top of the stack.
If you want to remove a deck of view controllers from a navigation controller, you should use the pop method on their root:
navigationController?.popViewController(animated: true)
This will pop everything out of the stack and you will be able to replace the root with other controllers.
This other answer: How to detect if view controller is being popped of from the navigation controller? expands on the different options you have to detect when your controller gets popped e.g. use isMovingFromParentViewController.

Related

UITabBarController's first view of type UINavigationController not acting quite right

So I've got a UITabBarController as a view in my app, the first tab of which is a UINavigationController. I have a button inside this view that pushes another, custom view onto the stack:
myViewController *vc = [[myViewController alloc] init];
[self pushViewController:myOtherView animated:YES];
The class myViewController has things that are supposed to happen inside of both -viewDidLoad and -viewDidAppear:(BOOL)animated, but if I hit my button right after the UITabBarController view appears, neither of those methods seem to be called. And even stranger, when I hit "Back", the view does not animate away, but rather the view underneath it in the stack just pops back into place.
However, if I go to another tab in the tab bar, then go back to the first tab, then hit my button again, my custom view controller animates in, -viewDidAppear:(BOOL)animated is called, and it animates out of view upon hitting "Back" like it should. Unfortunately, -viewDidLoad is never called.
I'm really trying to get away from using Interface Builder for everything; I want to create this view controller purely programatically, but these weird issues aren't helping. Can anyone think of a reason for this bizarre behavior?
Edit: Even if I create my view controller via IB, this behavior still occurs. What's the deal? Do I need to do something to the UITabBarController?
Edit #2: Here's how I want my views to bet set up:
UITabBarControler
Tab 1: UINavigationController
UIViewController to be pushed onto the stack
UIViewController to be pushed onto the stack
etc (possibly more UIViewControllers)
Tab 2: UIViewController
Tab 3: UINavigation Controller
UIViewController to be pushed onto the stack
UIViewController to be pushed onto the stack
You don't say what kind of object contains the code you posted but, if it's handling a button action, it's probably a custom view controller that's managed by your navigation controller. If that's true, then you'd want [self.navigationController pushViewController:myOtherView animated:YES];.
(If self is some other kind of object or self.navigationController is nil, then you would need to add some more details about your current view controller structure.)

iPhone NavigationViewController explanation

I have been using navigation view controller for some time now, and it really does great job. Problem is I don't fully understand it. Maybe some experienced members can shed some light on this topic. I have several questions:
1) Every class that extends UIViewController, has a property navigationController. Apple doc states this -"Only returns a navigation controller if the view controller is in its stack". Does this mean that this property is nil, if this controller is root controller.
2) When using method [self.navigationController pushViewController:nextController animated:YES] nextController is pushed to stack. If you then call self.navigationController inside nextController will navigationController property be nil?
3) Does every navigationController have its own stack, or there is shared stack for all controllers?
4) Finally what happens to items on the stack if you dont pop them, but release navigation controller? Lets say you do push, push, push, and then do release on navigationController. Do these objects stay on the stack or are they destroyed?
1) yes
2) no
3) Every navigationController have its own stack
4) When you push a view controller, navigation controller retains it. When navigation controller is released, then it releases all view controllers in the stack.

How to switch between two view controllers

I have two UIViewControllers, vc1 and vc2.
I want to switch between them. But before loading the view of the new view controller, I want to destroy/release/remove (I'm not sure abt the correct word to use here) the previous viewcontroller.
For example, when I am switching to vc2 from vc1 ,I want to destroy vc1 completely, so that when I return to vc1 from vc2, vc1 will be loaded from scratch (i.e. viewDidLoad will be executed).
Which type of view switching should I opt for?
presentModal...
addSubview.
I am not using a navigation controller.
Currently I am using the presentModal... method, but when I use dismissModalViewcontroller on the newly presented view controller, it doesn't show up a new instance of the previous view controller. Instead, it shows the already running instance of it.
I want the viewDidLoad method of the previous view controller to run when I dismiss the newly presented view controller.
What exactly needs to happen in viewDidLoad?
You also have viewWillAppear available to you, so it could be that you could move the required functionality there and still use the modal presentation.
See this answer. You can do this with or without animation.
Animate change of view controllers without using navigation controller stack, subviews or modal controllers?

Get to the view controller that pushed the visible view controller

I have an app that has a UINavigationController that pushes a UITabBarController into view. This UITabBarController has four tabs, one of which shows a custom UIViewController, an instance of EventInformationViewController. A button in this custom view controller in turn pushes another custom view controller EventRatingAddViewController into view. An action in this view controller should invoke a method in the EventInformationViewController instance. The following code makes the app crash instead:
// get the index of the visible VC on the stack
int myIindex = [self.navigationController.viewControllers indexOfObject:self.navigationController.visibleViewController];
// get a reference to the previous VC
EventInformationViewController *prevVC = (EventInformationViewController *)[self.navigationController.viewControllers objectAtIndex:myIindex - 1];
[prevVC performSelector:#selector(rateCurrentEvent:)];
I thought that the viewControllers property kept an array of all VCs on the navigation stack, so the index the currently visible one minus one should point to the VC that pushed the currently visible VC into view. Rather, it seems to point to my UITabBarController:
-[UITabBarController rateCurrentEvent:]: unrecognized selector sent to instance
What is up with that and more importantly, how do I get a pointer to the VC that pushed the currently visisble VC into view?
EDIT: I ended up creating a delegate protocol for the EventRatingAddViewController and assigning the EventInformationViewController as delegate. This works well - still I am thinking there should be a way to access the pushing VC through the navigation stack.
I'm pretty sure that that UITabBarController did indeed push your current view controller, but that what you are looking for is the view controller of one of this UITabBarController's tabs, the view controller that was visible in the UITabBarController at the time this UITabBarController pushed your view controller on the navigation stack. Possibly this UITabBarController pushed your view controller on the stack, because it was told to do so by the visible tab's view controller, so it would be something like this: [self.tabBarController.navigationController pushViewController:someViewController];.
The way to find out what view controller was shown in the UITabBarController at the time your view controller was pushed on the stack, is to use the .selectedViewController property, so that would result in something like this:
// get the index of the visible VC on the stack
int currentVCIndex = [self.navigationController.viewControllers indexOfObject:self.navigationController.topViewController];
// get a reference to the previous VC
UITabBarController *prevVC = (UITabBarController *)[self.navigationController.viewControllers objectAtIndex:currentVCIndex - 1];
// get the VC shown by the previous VC
EventInformationViewController *prevShownVC = (EventInformationViewController *)prevVC.selectedViewController;
[prevShownVC performSelector:#selector(rateCurrentEvent:)];

View controller chain

I have a Navigation Controller with a root table view which has several links. Tapping each link moves to the next view (by pushing it to the navigation controller's stack). But suppose that in that "next view", I have a UIButton that should take me further to another view (by pushing on to the same navigation controller's stack)...
View Controller-->first view-->second view-->third view..........
Now, I can easily access the Navigation Controller when I deal with the first view (and successfully push it to the Navigation Controller's stack) because it has been instantiated in the same file itself. What my real doubt is--How do you access a Navigation Controller in a far off view controller (eg, the third view or fourth view etc)? Please note that I am not using any separate delegate. All the Navigation Bar methods have been implemented in one file and connected to the Navigation Controller via an outlet.
When you push a ViewController onto a NavigationController the ViewController will automatically have it's navigationController property set. This means you can access the same NAvigationController no matter where you are in the stack.
-Update-
navigationController
In every UIViewController you can access that property.
So to in any other UIViewController that has been pushed onto the stack you should be able to just do this:
[self.navigationController pushViewController:othercontroller animated:YES];
Look at the documentation for UIViewController to see what other magic properties you have available.