Problems with UINavigationController nested within moreViewController - iphone

I have UITabbarMoreController which contains a few UINavigationControllers. These UINavigationControllers then contain my custom view controllers.
So the hierarchy looks something like this:
UITabbarController
- UINavigationController
-> my custom UIViewController
- ...(other children of UITabbarController look the same)
From within my custom view controller I call [self parentViewController] which should return a UINavigationController. And this really happens, I really do get a UINavigationController, BUT only when the particular UINavigationController is NOT inside moreNavigationController.
Since I have many children controllers in the tabbar controller, the tabbar controller creates a moreNavigationController. If I open a viewcontroller that is under moreNavigationController and call [self parentViewController] it returns a mysterious object with class UIMoreNavigationController
I really need to get the UINavigationController that is parent of my view controller, not the UIMoreNavigationController. Also, I tried using [self navigationController] with the same result. How can I get reference to the real closest parent of my viewcontroller? Thanks for help in advance!

In short, you can't.
Apple always try to optimise their code. One of their optimisation here is to check whether the ViewController displayed in the list of its UIMoreNavigationController is of type UINavigationController. Because UIMoreNavigationController itself is a UINavigationController it does not want to create another UINavigationController. It's trying to avoid UINavigationController nesting.
When you look at the header file of UIMoreNavigationController you will notice it has a variable
UINavigationController* _originalNavigationController; which is accualy the original UINavigationController that you created and that you want to access. Unfortunetly you cant since UIMoreNavigationController is private.
Workaround (ugly)
Pass the reference of your NavigationController to its children when you push them on it's stack.

Related

Calling pushViewController from child controller function

I have a root view controller that subclasses UINavigationController. It loads in a child view with a UIButton. When that button is pressed I want to make a call from the child view's corresponding view controller (lets say ChildViewController) to the UINavigationController's pushViewController: method in the parent controller.
How is this possible without directly referencing the parent view controller? Is it achievable using a standard protocol method or do I have to create my own?
Every UIViewController has a property called navigationController. If a UIViewController is a part of a UINavigationController's stack, you can use the navigationController property in the following manner:
[self.navigationController pushViewController:yourNextViewController animated:YES];
There's no need to access the rootViewController only for pushing a new ViewController on the stack. This could get really awful if you had big navigation stacks.
By the way - Apple states that UINavigationController is not intended for subclassing. Usually, it is a good idea to listen to their warnings and directions, so you may want to revisit the subclassing approach again.
using a subclassed UIViewController which is loaded to the UINavigationController's stack may prove a better approach.
Hope this helps.
The child UIViewController that contains your child UIView and UIButton should have the parentViewController property. You can use that to get a weak reference to your UINavigationController where you can message pushViewController:animated:

obtaining UINavigationController in an UIView

a custom tab bar controller I am using applies the ViewController or UINavigationController like this: UIViewController* viewController = [data objectForKey:#"viewController"];
I dont knw exactly how it works but "viewController" comes out as a UINavigationController. Next, the custom tab bar controller class adds a tag like so, viewController.view.tag = THE_TAG;
Retrieving the controller is uses UIView* currentView = [self.window viewWithTag:SELECTED_VIEW_CONTROLLER_TAG];
This part is where I get confused because now when I nslog this
"currentView" I get a UILayout etc... instead of my UINavigationController. I'm assuming it applied the tag to the UIView that contained the nav controller?
How do I reference the UINavigationController within this UIView?
In the above what is THE_TAG, and can you confirm that it is unique (i.e. not zero, and not matching something being used elsewhere by the same mechanism)?
I'd be pretty wary overusing tag since there is no easy way to gaurentee globally unique tags, and when using something like self.window viewWithTag you could see just about every view in the app being checked.
It seems like you have a view and a viewController confused. A UINavigationController is a subclass of UIViewController. It is not a subclass of UIView. UIViewControllers do have a property which is a UIView class. It is probably this property that you are accessing when you use viewWithTag: . So maybe, when you use that method, you are not accessing the UINavigationController but the UINavigationController's view property (which is actually something you should probably not be messing with.)

How can I set navigationController property of subview-viewController?

I have a UIViewController, and it has another UITableViewController as subview.
Table view is not visible, but in sometime it is visible by user's action. (for example, search table view.)
so, two new controller has no relation actually, (but UIViewController has UITableViewController as member variable), but only its view has parent - subview releation.
In this situation, user click the cell item in the table view, I have to push new view controller to navigation controller.
but only parent UIViewController is in navigationController, subview UITableViewController is not.
so code
[self pushViewController:viewController animated:YES];
fails in UITableViewController.
I solved this problem by passing navigationController instance of parent UIViewController to UITableViewController as property named parentNavigationController, and I called
[self.parentNavigationController pushViewController:viewController animated:YES];
instead.
It is solution that is not so bad, but I thinks it is some confused, I want to know is there more clearing solution.
How do you programming in this situation, friends?
I think parent-sub view relation of two view controller is confused, in the first. Is it better if I manage two views in one viewController? If then, view property of UITableViewController can be not table view? (and that view has table view as subview)
Thanks for your support, in advance.
Actually the concept is to push view controllers to the navigation stack of navigation controller. And UIViewControllers can only present modal view controllers, they can not push view controllers. So, sending pushViewController message to self(which is a UIViewController) is wrong. You can push a view controller using [self.navigationController pushViewController:anotherViewController];
In UITableViewController controller use self.navigationController instead of self.
[self.navigationController pushViewController:viewController animated:YES];

How should I manage swapping a UINavigationController in and out of another UIViewController?

Here's what I have:
A MainWindow.xib file configured with one UIViewController (subclassed to RootViewController). This nib gets loaded at application launch.
RootViewController has two ivars, a custom subclass of UIViewController and a UINavigationController. Both of these are loaded from nibs.
When the application launches both ivars are initialized from their respective nibs, and then the UIViewController.view is added as a subview of RootViewController.view.
Inside UIViewController's view I have a control that triggers an animated swap of UIViewController and UINavigationController. This is where the problem comes in. The swap animates, but the UINavigationController's views are not properly displayed. I get a Navigation Bar with no title, and nothing else.
The UINavigationController nib and underlying functionality have been tested in a stand alone project, but there was no RootViewController.
So, my question is, can I even do this? I've successfully swapped other view controllers like this, but never a UINavigationController. I've seen some documentation that leads me to believe this might be a fools errand, but I haven't convinced myself of that yet.
Solution (Kinda):
I found a solution (workaround? hack?), but it leads to some more questions. I nixed using a Nib for the UINavigationController. Instead, I loaded my UINavigationController's rootViewController from a Nib and then created the UINavigationController programmatically with initWithRootViewController:.
NavRootViewController *navRoot = [[NavRootViewController alloc] initWithNibName:#"NavRootViewController" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:navRoot];
[navRoot release];
This works as I expect. Which leads me to the conclusion that the rootViewController property of the UINavigationController wasn't being set properly when I loaded navigationController from a Nib. And the question is, why? Should it?
Also, when you see something like this happening in one case, but not another, it can be beneficial to either create a subclass and make your nib point at that subclass, or if you already have a subclass use that.
In the subclass, override all the various init:, initWithNibName:bundle:, viewDidLoad:, viewWillAppear:, viewDidAppear: and any other appropriate methods, and in those override, just NSLog("") something about which method it is (with param values perhaps) and call the super implementation.
This will give you an observable "track" of which methods are called in which order, and you can set a breakpoint to see where that call comes from.
This will give you enough information to find missing method calls, and then you can pursue the correct problem either here, or through filing a radar or ...
In some cases, viewDidLoad and viewDidAppear or awakeFromNib may need to be called each time you add the UINavigationController back into the stack of UIViewControllers. It seems that when the typical code executes out of your AppDelegate, that the Window, or something behind the scenes is doing something special for UINavigationController that presentModalViewController doesn't do.
I think you may have missed a conceptual point.
A UINavigationController controls view controllers instead of views. It controls when and where view controllers themselves are loaded. The views themselves are loaded only as a side effect of the pushing and popping of their respective controllers.
Therefore, putting a navigation controller inside of a view controller seldom makes much sense.
If I understand what you are trying to do correctly, you should have the RootController actually set as the rootController property of the UINavigationController (yes the nomenclature is extremely confusing.) Then when your swap event occurs, you should have the navigation controller push the next view. The RootController view will disappear to replaced by the other. then you can repeat the process for an arbitrary number of view controllers.
Only in the case of a tabbar would you want a navigation controller to be a property of a view controller. Even then it should be at the top the tab's hierarchy.

What is the best way to change my UIViewController to a UINavigationController

I just realised that my 'root' viewController should have been a UINavigationController. Now I want to change this to be a UINavigationController instead and just curious what my best option would be. I built this view and all other views using IB if that makes a different.
I'm mostly worried that I would have to do a lot of copy/pasting and recoding to get everything right or will be it be as easy as manually editing my controller and change the extension to UINavigationController.
Thanks
You do not convert an existing view controller into a navigation controller.
Even though UINavigationController is a subclass of UIViewController, it's task is the management of other view controllers, not the management of views themselves. You don't swap them out one for the other. Instead, you set UIViewControllers to be controlled by the nav.
To add a nav to a project in IB, open the xib and drag over a UINavigationController. Then set the navigation controller's rootControlller property to the existing UIViewController.
And you're done.
You can also "Embed" the Navigation Controller by selecting the View Controller in IB then select Editor->Embed In->Navigation Controller
You can instantiate a trivial not-subclassed UINavigationController and give it your original UIViewController as its root controller, like:
YourRootViewController *rootViewController = [[YourRootViewController] initHoweverYouInitIt];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
and maybe an [rootViewController release];, depending on how you are going to manage memory.
UINavigationController has an 'is-a' relationship with UIViewController, so you should be able to change its class type in Interface Builder with no additional changes.