How do I instantiate navigationController while passing data to viewController? - ios

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

Related

UINavigationController of UIViewController is nil, after pushViewController

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).

Switch from View Controller to Tab Bar Controller in a Storyboard xCode

I am working on an app in xCode 5. This is a first at using the Storyboard. My app starts with a simple username/password login screen. Upon successful login, I want to programmatically switch from this login View to my Tab Bar Controller with the tab index set at 1.
I do not have a custom class for my UITabBarController. I can build one if need be. Can someone help get me started or point me in the right direction?
Funny how typing out the question can help you solve it. Here is the code I used in case someone has the same issue in the future
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
UITabBarController *vc = (UITabBarController *)[storyboard instantiateViewControllerWithIdentifier:#"UITabBarController"];
[self presentViewController:vc animated:YES completion:nil];
[vc release];
If you have only one storyBoard in your project then you can also use simply
self.storyboard
The best way to do this is to set your rootviewController to tabBarcontroller as soon as you authenticate the user. Here is how you can do this in swift.
let tabBarController = self.storyboard?.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
Where TabBarController is the storyboard id of your tab bar controller. It could be anything whatever name you have given it.
In storyboard; drag a segue from your ViewControllers button (or File's Owner to connect a method) to your TabBarVC. Choose modal style and animation if you wish.
Add a new custom class for UITabBarController and assign it to TabBarVC in storyboard.
In it's implementation file; put self.selectedIndex = 1;

How to get out of a UIStoryboard initialized in code

I have a main container controller that initializes child view controllers.
I'm trying to learn to use UIStoryboards tho and I'm stumped as to how to get out of a storyboard.
Here's the flow:
Once I get to the end of a storyboard's scenes, how should I get out of storyboard and back to my container controller?
Should I keep a pointer to the storyboard? What would I do with it?
Should I keep a pointer to the initial view controller (which is the one I explicitly add as child)? It's .view won't be on screen at the end so I don't know what I would do with that either.
Try looping through the childViewControllers
for (UIViewController *vc in self.childViewControllers) {
// do something
}
What do you mean "out of a storyboard". You don't need to save a pointer to your storyboard as self.storyboard should work in most cases. If you use a UINavigationController as your entry point in your storyboard with your initial view controller as it's root view, all you have to do to get back to your initial controller is
[self.navigationController popToRootViewControllerAnimated:YES];
Also, you can get a reference to your storyboard like this as well:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];

Pushing a View Controller using another UINavigationController

This is how my app looks like. I've subclassed UINavigationController in a way that when you tap a button on something similar to a navigation bar the MenuViewController slides out. How can I push \ present one of the my VC1, VC2, VC3 into ContentViewController from didSelectRow that's inside the UITableViewController menu?
-> SlidingViewController
/ \
Container Container
| |
MenuViewController ContentViewController
| |
UITableViewController SubClassed UINavigationController
| | | |
VC1 VC2 VC3 VC4
Usually I do something like the next code but with the situation above I'm not sure how I can push a new view controller if i'm not in the same UINavigationController.
NSString * storyboardName = #"MainStoryboard_iPhone";
NSString * viewControllerID = #"ViewID";
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
MyViewController * controller = (MyViewController *)[storyboard instantiateViewControllerWithIdentifier:viewControllerID];
[? presentViewController:controller animated:YES completion:nil];
Edit:
Possible solution that works but I'm not sure if that's a good way to do it. I've used a singleton to save my Navigation Controller and use it later.
Inside my ContentViewController:
[DataManager sharedDataManager].navController = self.navigationController;
Inside my didSelectRow in MenuViewController :
[[DataManager sharedDataManager].navController presentViewController:controller animated:YES completion:nil];
You don't want MenuViewController to have knowledge of anything in the view controller hierarchy other than itself and its children. You have two options that allow for this good design.
1. Use delegation
First, you want to have a MenuViewControllerDelegate that has, for example, menuViewController:didSelectOption:. When something happens in the menu, MenuViewController will send an appropriate delegate message to its delegate object.
Now to listen for that delegate message, you will need a controller object (could be a view controller or just a regular NSObject) that conforms to MenuViewControllerDelegate and sets the delegate of MenuViewController to itself. This controller object will then handle any delegate messages that MenuViewController may send, e.g. push a new view controller onto ContentViewController when a menu option is selected.
2. Post notifications
This is the more fragile of the two. You would use notifications if you have lots of things all over your view controller hierarchy that need to know when something happens in the menu.
So MenuViewController would post a notification, perhaps named MenuViewControllerDidSelectOptionNotification, and then any interested receivers that have registered for that notification would then receive the notification and do their thing.
In your App Delegate (or in response to your edit, any persisting singleton will work, yes), create properties to hold your View Controller instances.
#property (nonatomic, strong) MenuViewController *menuViewController;
... etc for all your vc's.
Whenever you initialize them later, set this property on the App delegate to hold the VC.
MenuViewController *menuVC = [[MenuViewController alloc] init];
AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
appDel.menuViewController = menuVC;
Now you can get this instance any time you need to present it. You can also set any properties on the menuViewController that you want to keep in memory.
Note: Be conscious of being a good memory citizen. Write didReceiveMemoryWarning methods to release anything you can easily reload when the VC becomes visible again so that you don't crash your app from holding too many things in memory at once.
You should have one class (possibly the app delegate) which creates your container structure. It has access to the view controllers that are created. Your table view controller and navigation controller may not both exist initially but the class which creates everything initially should 'build the bridge' between the structures by passing a reference of the navigation controller or the content view controller to the table view controller or the menu view controller. That reference should be stored in a property and will allow you to cleanly present your view controller.
This kind of setup is preferable to navigating through parentViewController or similar links as that will lead towards tying you to a particular structure in a non-transparent way.
Or, in your storyboard, you can just add an IBOutlet property and make the connection directly there (if all of the view controllers are created up front when the storyboard is loaded).

General way to pass data to UIViewControllers when using a UITabController

I still haven't grasped this transfer with the structure below. I have read many posts, and have seen the same unanswered post by others, but no resolution.
I will try to simplify the question to make it easier for all.
The structure of the project is:
UITabbar with tab1 and tab2
Tab1 has a Nav controller-->ViewController1
Tab2 has a Nav controller -->ViewController2
In viewcontroller1 (tab1) I have object X.
In ViewCOntroller2 (tab2) I want to display object X.
Don't worry about displaying, that's the easy part.
Question: How do you pass object X from tab1 to tab2. (what is the general pattern).
If you want to do it using prepareForSegue, is this ok, or is there a better way.
If using prepareForSegue, where do you drag the segue to?
The tabbarcontroller
OR*****
2. to the second VC
Hopefully this is clear enough. With this in mind how would you perform the transfer?
Using the segue 1:
I tried doing this:
//(From View controller 1)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"CreateObjectXToDisplayObjectX"])
ViewController2* vc2 = [[ViewController2 alloc] init];
UITabBarController* tbc = [segue destinationViewController];
vc2 = (ViewController2 *)[[tbc customizableViewControllers] objectAtIndex:1];
//Crash here with with [MainNavigationControllerDesign setViewController1Delegate:]: unrecognized selector sent to instance 0x1064ef70'
vc2.viewController1Delegate=self;
vc2.objectXAtViewController2 = _objectXFromViewController1;
}
}
So, how is this Object X transfer accomplished?
Thank you in advance
You don't want to use segues in this way. Segues always instantiate new controllers when you go to them, but you already have these controllers embedded in the tab bar controller. If you were setting this up in code, I would say use a delegate, but if you set this up in IB, it's hard to do that. From VC2, you can get a reference to VC1's navigation controller with self.tabBarController.viewControllers[0]. VC1 will be that navigation controller's topViewController, so, putting that together, and adding a cast, you can access VC1 like this:
ViewController1 *vc1 = (ViewController1 *)[self.tabBarController.viewControllers[0] topViewController];
Once you have that reference, you can access any of vc1's properties. Don't forget to import ViewController1.h into ViewController2's .m file.

Resources