I have an application with a UINavigationController that contains a root UIViewController subclass (PagingViewController) which manages and acts as the delegate for a horizontally paging UIScrollView. On each of the pages of this UIScrollView, there is a vertically scrolling UITableView, each managed by its own UIViewController subclass (PageViewController). It looks something like this...
UINavigationController
PagingViewController (root VC of navigation controller)
PageViewController (instance 1)
PageViewController (instance 2)
PageViewController (instance 3)
HowToPushThisViewController (this is what I'd like to push onto the current root, PagingViewController, from one of its sub view controllers)
What I'd like to do is, in the didSelectRowAtIndexPath: method of the PageViewController (UITableView delegate), push a new UIViewController onto the UINavigationController (on top of the PagingViewController).
The issue is that the navigationController property of the PageViewController is nil, so I can't push it there. I assume this is nil because it is not DIRECTLY on the UINavigationController's stack, but contained by the PagingViewController which is on the stack. I tried setting the navigationController property of each PageViewController before adding it to the PagingScrollView, but it seems to be read only (How does the UINavigationController set itself?).
I feel like the nested UIViewControllers is sound architecture (each one is needed to manage and act as delegate for its views), and it's reasonable to want to push navigation from a sub controller, so I'm not sure the best practice for accessing and pushing from the nested controller. Hopefully there is something simple I'm overlooking to access and push onto the root naviagtion controller.
The logical thing to do would be to set the page view controller's parentViewController property but alas, it is read-only too. Apple's does not encourage custom hierarchies of view controllers (besides the supported hierarchies with tab bar, navigation and split view controllers).
In your case, I would define a custom parentVC propertyy on the PageViewController class that the PagingViewController sets to itself when it instantiates its children. The PageViewControllers can then get to the nav controller via
self.parentVC.navigationController
Related
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.
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:
In my appDelagate I have a UIViewController called "FrontPage" which is basically a log in screen. Once the login has authenticated it removes itself from the superview and creates a tabbarcontroller, navigationcontroller (inside tabbar), and various UIViewControllers in the NC and on their own in the tab bar. I then push my TabBarVC.view to the windows subView.
It works but I was hoping after I set the windows subview to TabBarVC.view I could release the TabBarViewController to dealloc it and the appdelagate would own the TabBarVC, but when I do it's crashing.
As I'm typing this I'm realizing I never pass the actual TabBarVC, just the view but is there a way to do this?
Also if I completely FUBAR'd this up let me know.
You should set the window's rootViewController property to your UITabBarController instance similar to this:
// set the tab bar controller as our root view controller
[self.window setRootViewController:tabBarController];
To clarify, this will add the TabBarController, its view and all of its subviews to the window's view hierarchy for you and I would recommend you use this method for your login view controller as well.
You could make your UITabBarController an IBOutlet to the app delegate (or just keep the code you have that generates it). Make it a retained property of the app delegate, synthesize the property, and either create the UITabBarController in the app delegate (self.tabBarController = ...) or if you use an xib make the IBOutlet connection from the UITabBarController to the app delegate in the xib.
You could add the UITabBarController to the app's window, and then add the FrontPage UIViewController on top of it. Once you remove the FrontPage from the window, the UITabBarController will already be present underneath it.
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.
I have been stuck on this for a few days now and it is killing me... In my viewDidLoad event, I am trying to programmatically add a full screen UINavigationController to a subview of my view controller. So far, I have only succeeded in doing two things...
1) Only a grey screen shows up
OR
2) I get something that resembles a navigation controller added to the view controller, instead of being my navigation controller from a XIB it is just a generic one... even though I loaded from the XIB. Oddly enough it is always shifted 25 pixels downward and slightly cut off.
I have read every single link on google and I can't seem to figure this out. I just created a new viewcontroller... added a UINavigationController to it... try to load that view controller and it messes up.
Any help is greatly appreciated!!!!
Instead of having the UINavigationController be a child of some other view controller, make the UINavigationController the root controller itself. The navigation controller is one of the special "container" view controllers, and it generally wants to own the whole screen and be at the root of the controller hierarchy (except in certain circumstances).
Try something like this:
UINavigationController * rootNavController = [[UINavigationController alloc] initWithRootViewController:myRootControllerInTheNavController];
[window addSubview:[rootNavController view]];
Which will obscure any existing views with the nav controller (those existing things will still be there when you -removeFromSuperview the nav controller's view). The nuclear option is to set your UIWindow's rootViewController property with the nav controller, but it sounds from your comment that this may not be what you want to do here.
Possibly a cleaner approach: If it accomplishes what you want, I believe you could also take your nav controller and present it modally (see docs for uiviewcontroller) from whatever the current view controller is. Set the transition appropriately, and while you're in the nav stack, the nav controller will be visible.