I'm using the ECSlidingViewController for my navigation menu, whenever I want to push a UIViewController from this menu, the UINavigationController of the pushed UIViewController is always nil.
The UINavigationController is initialized, the NSLog output shows the following <UINavigationController: 0x8a80770> address. When I call the method pushViewController:animated the UIViewController gets pushed but the UINavigationController is nil, therefore I can't see the UINavigationBar in this controller.
Here is the code snippet I'm using for this:
RecommendationsViewController *rvc = [self.storyboard instantiateViewControllerWithIdentifier:#"RecommendationsViewController"];
[self.transitionsNavigationController pushViewController:rvc animated:NO];
self.slidingViewController.topViewController = rvc;
In viewDidLoad the transitionNavigationController get's initialized with (please note the slidingViewController is from the ECSlidingViewController project on github https://github.com/ECSlidingViewController/ECSlidingViewController and is of type ECSlidingViewController):
self.transitionsNavigationController = (UINavigationController *)self.slidingViewController.topViewController;
Thanks for any help!
I think you have misunderstood how this is suppose to work.
The UINavigationController has to be the topViewController.
Don't reassign the topViewController after you do a push. By doing this:
self.slidingViewController.topViewController = rvc;
All that is going to do is set the current window to display that UIViewController, thats why you didn't see the nav bar, the app needs to display the UINavigationController which in turn will manage a list of UIViewController's
The navigation controller handles a stack of viewControllers, just push the new UIViewController and nothing else
There is a related issue where a Navigation controller's topViewController will forget that it is attached to a navigationController.
My Storyboard setup is: ->NavigationController->ViewController
The connection between NavController and ViewController is "root view controller".
I have set a storyboardID for each of these view controllers.
I have a view management class "ViewManager" that contains weak references to all storyboard views, which I obtain using:
_rootNC = [self.mainStoryboard instantiateViewControllerWithIdentifier:#"NavController"];
//ViewController gets auto-attached to the NavController, and so viewController.navigationController == NavController
_firstVC = [self.mainStoryboard instantiateViewControllerWithIdentifier:#"ViewController"];
//Instantiating the ViewController again clears its navigationController property, and so viewController.navigationController == nil
I suppose one shouldn't gain a hook into Storyboard Instances by reinstantiating the views. I'd appreciate if others would share their best-practices for obtaining weak references to storyboard viewControllers in such a way that I could control them in a single viewManager class. (I'm leaning toward setting viewManager.rootNC from within NavigationController's viewDidLoad).
Related
So I have a tabBar. Clicking a certain tab will take the user to a viewController which is embedded in a navigationController. In order to have the navigationController included, I instantiate the viewController using
myViewController = [storyboard instantiateInitialViewController];
rather than
myViewController = [storyboard instantiateViewControllerWithIdentifier:#"myID"];
The later code pushes the viewController but without the navigationController
This is fine up until the point I want to pass data to the instance myViewController. Thing is, I can't pass data with the instance referring to the initial viewController (which is the navigationController), but I can do it using an instance referring directly to myViewController. In other words:
This works in order to get data (but no navigationController):
viewController = [storyboard instantiateViewControllerWithIdentifier:#"experienceID"];
((ExperiencesListViewController*)viewController).experiences = self.experiences;
and this crashes if I try to add data, but gives me a navigationBar if I exclude setting data:
viewController = [storyboard instantiateInitialViewController];
((ExperiencesListViewController*)viewController).experiences = self.experiences;
Hope I explain this well enough. Let me know if there is something I need to clarify.
EDIT
My first thought was using prepareSegue but that doesn't seem to trigger when moving between the tabs. Another thought is to access myViewController through the instance in some way, but not sure how.
That's because the initial UIViewController of that particular storyboard is a UINavigationController.
A simple way to get the VC that you want is getting such UINavigationController by calling:
navController = [storyboard instantiateInitialViewController];
just like you've done, and then:
navController.childViewControllers[0]
This will return the first VC of that particular navigation controller (assuming, of course, that it contains solely the VC that it's embedded in), which is probably your ExperiencesListViewController
In my storyboard for a container class, I have created a full-screen Container View with an embed segue that's attached to a UINavigationController. I'm trying to test this by pushing a viewController programmatically like below:
UIViewController* test = [[UIViewController alloc] init];
test.view.backgroundColor = [UIColor redColor];
UINavigationController* navC = [[RootController mainStoryBoard] instantiateViewControllerWithIdentifier:#"containerNav"];
[navC pushViewController:test animated:NO];
navC is successfully initialized as the navigation controller, but the pushViewController call seems to do nothing. I still just see an empty navigation controller on the screen (default navigation bar with black view). I also tried a similar test with the setViewControllers method, which also failed. Are navigation controllers not meant to be embedded into container views?
UINavigationController needs a UIViewController at the bottom of its stack from when it is first created, (and it can't 'pop' that one). In other words it can't begin with an empty stack, you should be adding/embedding one viewController inside it in the xib/storyboard, then I expect it'll work with what you're doing here
I think you may need to just set the viewControllers property on the Nav Controller, like this:
[navC setViewControllers(#[test] animated: NO]
On a UIViewController instantiated by a UITabBarController, when I run the following code,
let destination = self.storyboard?.instantiateViewControllerWithIdentifier("test")
self.navigationController!.pushViewController(destination!, animated: true)
navigationController return nil. How can I get navigationController instance to push a view controller?
As #Paulw11 said, what was missing was embed the UIViewController in a navigation controller. To do that, drag-and-drop a Navigation Controller to the Storyboard and CTRL + drag from that Navigation Controller to the UIViewController, chosing the relationship root view controller.
Your source UIViewController instance needs to be embedded in a UINavigationController in the storyboard scene in order to provide a navigation controller instance to push to.
https://stackoverflow.com/a/32959896/3810914
Exactly. If you did, the error would be resolved
I have a regular UINavigationController with a couple of views attached, which are working perfectly fine. Its RootViewController has a custom Menu-button on the top left, at the same place as the "Back"-button is on the attached views. When clicking this menu-button, the menu appears and presents five options.
Obviously, by clicking one of these option, you would be presented with the ViewController for that option.
I want to completely 'forget' the current ViewController, and move on to this new controller. Usually, I would do something like [self presentViewController....]; or [self.navigationController push..];, but in these methods the current ViewController will, I think, always exists 'below' the new presenting viewController (as you would return to this instantiation if using [self dismissViewController..];, I don't want this).
In the presenting ViewController there will be a menu-option to return back to the original controller, but I still want this to be a clean instantiation of it, and not just popping. By thinking ahead in time, I figured I would potentially create an infinite number of ViewControllers on top of each other by using the methods I know of this way.
I entered the world of iOS after the era of ARC began, so I have no clue how to release or deallocate such views, which I assume has relevance here.
The second View Controller is also supposed to be a root in a UINavigationController, and I'm not sure if it's best to use the same UINavigationController, or if I should present a new one, and dismiss the old. Essentially, I would like to replace the Navigation Controller's rootViewController from the rootViewController, but I don't see how that would be possible. Or possibly push to ViewController2, and then popping the rootViewController out of the hierarchy, leaving the new ViewController as the root, but then I assume I'd have problems with the navigational back-button(if it's even possible).
I figured it's just as easy to let ViewController2 be root at its own NavigationController, and presenting this NavigationController from ViewController1. The problem is, I want to completely remove everything that has to do with ViewController1 and its NavigationController from memory after presenting ViewController2, but I have no idea how.
I'm open to other solutions to my situation, but I'd also like an answer to how I can completely 'forget' a view after presenting another on general basis.
If you want to "forget" controllers, you can just replace the window's root view controller with a new one. The original one will be deallocated if you don't have any other strong pointers to it. I'm not sure I understand all of what you're trying to do, but for example, if you want controller 1 and controller 2 to both be root view controllers of a navigation controller, and you don't want controller 1 around when you switch to 2, then do something like this from controller one:
SecondViewController *second = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:second];
self.view.window.rootViewController = nav;
This will switch out the controllers, and controller 1 and its navigation controller will be deallocated (assuming that the only thing with a strong pointer to the navigation controller was the window, through its rootViewController property).
Create a menu UIViewController and add it as a root to the UINavigationController on launch. Add 1st UIViewController as a child controller to menu UIViewController when viewDidLoad of menu controller is called. When you click menu to show 2nd UIViewController, remove the 1st UIViewController from menu view controller and add 2nd UIViewController to child of the menu view controller. You can put NSLog in both, 1st and 2nd view controller's dealloc method to check if its released or not. Logic is like this
//inside menuvc class
#interface MenuVC{
UIViewController * currentVC; // current child controller to menuVC
}
-(void)viewDidLoad{
[self addChildController:firstVC]; //to add view controller 1 intially
currentVC = firstVC;
}
-(void)add2ndChildController{
[currentVC removeFromParentViewController];
[self addChildController:secondVC]; //to add view controller 2 when needed
currentVC = secondVC;
}
// dealloc of 1st vc
-(void)dealloc{
NSLog(#"first vc released");
}
I just wrote some sample logic of what I explained before, you have to generalize this logic if you feel its right for you. Hope it helps :)
I have setup a UINavigation controller that uses the AppDelegate as the main point of contact.
I have different methods which run such as presentHomeViewController, presentLoginViewController, which push the different view controllers to the Navigation Controller.
App Delegate - didFinishLaunching
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
self.window.rootViewController = self.revealSideViewController;
Is this the correct process for this?
- (void)presentHomeViewController {
// We start by dismissing the ModalViewConrtoller which is LoginViewController from the welcomeview
[self.welcomeViewController dismissModalViewControllerAnimated:YES];
// Check if the home view controller already exists if not create one
if (!self.homeViewController) {
NSLog(#"presentHomeViewController- Creating the Home View controller");
homeViewController = [[MyHomeViewController alloc] initWithNibName:#"MyHomeViewController" bundle:nil];
}
// Push the homeViewController onto the navController
NSLog(#"presentHomeViewController");
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
If I then add the following to a different class :
[self.navigationController pushViewController:accountViewController animated:NO];
No view is pushed to the stack, should I control all the movement within the AppDelegate as I have been doing, or is there betters way to approach this?
EDIT
Thanks for posting your code. So, to address your final question first, I don't recommend controlling your navigation stack from the app delegate. You should be controlling the stack from the view controllers that are the children of the navigation controller.
To that point, remember the hierarchy of view controllers: UINavigationController inherits from UIViewController, and UIViewController has properties defined for all the things you'd see in a navigation layout such navigation items and title. More importantly, it also has properties for its parent view controllers, the view controller that presented it, and its navigation controller. So, considering the hierarchy, your app delegate should only instantiate the navigation controller's root VC and the nav controller itself, and then subsequently set the nav controller's root VC.
From there, you should be pushing and popping other VCs from the VCs themselves. Remember, every VC has a property that's automatically set to point at the navigation controller it's a part of. That's why [self.navigationController pushViewController:] works. For instance, if I have a nav controller whose root VC is a UITableViewController, and tapping on one of the items in the table view pushed a new VC onto the stack, I would push that VC from the table VC and not from the nav controller class or the app delegate.
Sorry if that's confusing. Please let me know if that needs clarification and I'll give it my best. Otherwise, hopefully that gets you on the right track.