Loading/initializing secondary UITabBarController from/with nib - iphone

In the application I'm trying to develop I have UINavigationController as a root controller. I initialize views using pretty common code:
MySubclassOfViewController *vc = [[MySubclassOfViewController alloc]
initWithNibName:#"MySubclassOfViewController"
bundle:nil];
vc.title = #"A title";
[self.navigationController pushViewController:vc animated:YES];
[vc release];
After a succession of some views I want to load UITabBarController.
Is there a way to desing the nib file and create an instance of UITabBarController the same way as above?
I know I can do this programmatically or by explicitly declaring an outlet and connecting it with the controller in the nib. It's also possible to initialize the controller using something like
NSArray *objects = [[NSBundle mainBundle]
loadNibNamed:#"MySubclassOfViewController"
owner:self
options:nil];
self = [objects objectAtIndex:0];
[objects release];
But can I make it without extra work & typing?
Let's say I define a subclass of UITabBarConroller (although I know it's discouraged in the Apple docs, but just out of curiosity). When I make an instance of the subclass, can I somehow load the superclass part out of a nib?

I've solved my problem myself. After some research, I came to these conclusions.
It's not possible to do it the same way (i.e. UITabBarController *tbc = [[UITabBarController alloc] initWithNib...) Either there should be an extra outlet for the controller, or the controller should be explicitly alloc/init'ed (but not initWithNibName), or it could be instantiated using loadNibNamed. That's it. The reason for this is that the tab bar controller doesn't have outlets declared to connect them to children view controller. (For more details on loading nibs check Development Chaos Theory blog).
If the previous answer was 'yes', then all I had to do was to set the custom class in the Interface Builder to my subclass. This works for other objects (well, may be except the UINavigationController, which is not intended for subclassing as well). But since the 'simple' initialization of a tab bar controller is impossible, this is impossible too.
Any comments on this? :)

Related

UIView presented with Black screen, fix?

I call a View to be presented with the following code:
#import "infoView.h"
...
infoView *viewInfo = [[infoView alloc] initWithNibName:nil bundle:nil];
viewInfo.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewInfo animated:YES];
But when it is presented in run-time the view that is loaded turns out black.
Currently I am using storyboard, but I need to use this code, for it is a lot more efficient in my case, because I am dealing with multiple views!
It works fine if I connect it via StoryBoard.
I should be seeing 2 labels, 1 UITextView, and 2 UIButton.
The view was created using StoryBoard, when the .m and .h files for the view where created I did not add a .xib for it. And also it is linked through the "Custom Class" section in StoryBoard.
Thanks, hope someone can help!
It's generally pretty bad form to mock people who are taking the time and effort to help you.
Naming is important it makes your code easier to work with and allows other people to use it. Not following the conventions for the language you are working in is dangerous and means that your code is not compatible with other developers as things are interpreted differently.
If you look at the docuemntation for UIViewController you'll see this note in the initWithNibName:bundle: method description
If your app uses a storyboard to define a view controller and its associated views, your app never initializes objects of that class directly. Instead, view controllers are either instantiated by the storyboard—either automatically by iOS when a segue is triggered or programmatically when your app calls the storyboard object’s instantiateViewControllerWithIdentifier: method. When instantiating a view controller from a storyboard, iOS initializes the new view controller by calling its initWithCoder: method instead. iOS automatically sets the nibName property to a nib file stored inside the storyboard.
Therefore you are instantiating your controller wrong, the storyboard should be instantiating it. Which is done like this (naming corrected)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle bundleForClass:[self class]]];
InfoViewController *infoViewController = [storyboard instantiateViewControllerWithIdentifier:#"InfoViewController"];
infoViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self infoViewController animated:YES];
Side note
infoView is a bad name for the class not only because you didn't start with a capital but also because it's completely deceiving. Anyone reading this would assume that InfoView is a subclass of UIView not UIViewController.

iPhone Programming - Passing NSMutableArray between view controllers

I have a RootViewController and a DetailViewController where I am trying to pass a NSMutableArray from the rootView to the detailView. I have created an object of the RootViewController inside the DetailViewController and accessing it like the following
RootViewController *root = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
detailViewArray = [root.rootViewArray copy];
Note: Both the arrays are declared in the .h files; synthesized and then allocated and initialized array = [[NSMutableArray alloc] init];
Problem: I am not too sure why it still doesn't work. I have tried a lot of the solutions on the internet but it didn't quite work for me. The property for the root array is nonatomic, retain Is it something wrong with that? Do I need to change it to something or the method I am following is just not right.. Please if someone could help!
If you alloc/init the RootViewController inside the DetailViewController, you are creating another instance of the RootViewController. You are not getting the same instance (with data) of the rootViewController.
That said, even passing a reference of a viewController to another viewController to then poke at it's data is sort of bad. It creates tight coupling between the views.
Instead, if you need to get data, consider using a delegate to communicate between the views.
What exactly does delegate do in xcode ios project?
Tutorial:
http://www.theappcodeblog.com/2011/04/15/passing-data-between-views-tutorial-using-a-protocol-delegate-in-your-iphone-app/
Another option is to create a shared model (read up on model view controller patterns). It's typical in that pattern to create a model and share data by getting a singleton instance of your model:
MyModel *model = [MyModel sharedInstance];
Then, each view can set and read data from that same (singleton) instance of the model.
Which to choose? The model is better if many views share the same data. A delegate is appropriate for a couple views to communicate with each via callbacks.
Even there is an another way to pass array.
MyViewController mvc = [[MYViewController alloc] initWithNibName:#"MYViewController" bundle:[NSBundle mainBundler]];
[mvc getDataArray:<pass your array>];
[self.navigationController pushViewController:mvc animated:YES];
Here it will set a dataArray property first then it will push the view controller to MyViewController class.
This will be useful when you are very much concerning about memory. Because if you are using static (sharedInstance) then its scope persistance within the entire application scope.

I can make UINavigationController load only at 2nd level, not at Root View Controller

I tried looking for a similar problem but I could not find a similar question.
I am loading a UINavigationController in a UIView which is not (as in most examples) the MainWindow.
I created one new .xib called DocumentsViewController which is subclass of UIView (it has the related .m and .h files). And I created a DocumentsRootViewController.xib, which is a subclass of UITableViewController, which is supposed to be the UINavigationController's RootViewController.
I moved to DocumentsViewController and added a UINavigationController object in Interface Builder. Then I went to code, and added it as in IBOutlet, and connected it to the object.
In the ViewDidLoad, I execute the following lines:
DocumentsRootViewController *rootViewController = [[[DocumentsRootViewController alloc] init] autorelease];
rootViewController.title = #"Documents";
[navigationControllerDocuments initWithRootViewController:rootViewController];
[self.view addSubview:navigationControllerDocuments.view];
It shows the table as intended, but it shows a "Back" button to the "Root View Controller" (as in picture below).
Why? Shouldn't it already know that the rootviewcontroller has been set?
Thank you in advance to the ones that clarify this doubt
Giovanni
When you add the UINavigationController via the Nib it actually creates an instance of a UINavigationController inside the nib file with a default RootViewController set (of type UIViewController) and with a default title of RootViewController.
When you load the nib, this object is being created as part of loading the nib (i.e when you initialise DocumentsViewController) - so the navigationControllerDocuments outlet is already initialised as a UINavigationController with the default ViewController already set on it.
What I think is happening is when you call 'initWithRootViewController' - you are calling this on an already initialised object - so it is running the initialisation code again - pushing the second view controller (the DocumentRootViewController) onto the stack, but the default one that was created in the nib is already there.
What you should probably do is forget about creating one in the nib and initialise the whole thing programatically.
i.e. where you do:
[navigationControllerDocuments initWithRootViewController:rootViewController];
I suggest that you do an alloc and init instead:
[[navigationControllerDocuments alloc] initWithRootViewController:rootViewController];
Since you are doing this you really don't need to have the navigation controller added to the nib so if this works you should remove it from the nib since you are replacing it with this one in code.

UITabBar and more than 1 UINavigationController

I'm currently working on an app that require that I have different UINavigationControllers - So I'm using a tab bar and attempting to use a UITabBar to swap between them so I have in the app delegate a bit of code like so:
// Setting up the views for the tab controller
Friends *friends = [[[Friends alloc] initWithNibName:#"Friends" bundle:[NSBundle mainBundle]] autorelease];
WifiManager *wifi = [[[WifiManager alloc] initWithNibName:#"WifiManager" bundle:[NSBundle mainBundle]] autorelease];
UINavigationController *locationController = [[UINavigationController alloc] initWithRootViewController:wifi];
UINavigationController *friendsController = [[UINavigationController alloc] initWithRootViewController:friends];
//Set up the tab controller
tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers =
[NSArray arrayWithObjects:locationController, friendsController, nil];
//Add the tab bar to the window
[window addSubview:tabBarController.view];
This will compile and will load up the first UINavigationController but when I click on the other navigation controller I get:
*** -[NSCFData tabBarItem]: unrecognized selector sent to instance 0x1152b0'
The strangest part is I can use the tab controller with a single UINavigationController and everything works as it should but when I try and add the second one it fails miserably - has anyone got any ideas as to what I'm doing so wrong here?
Thank you in advance
James
Have you verified that each of the single view controllers (Friends and WifiManager) work when there is only one? It could be that your problem is not "two controllers", but "one controller that is broken."
That code should work.
The only thing I can think to suggest, is to not autorelease the view controllers you are creating.
I have an application that works in a similar way. The way I handle this is to abstract it a little more. That is to say, let the top level (below the default window and stuff) be only the tab-bar controller. I would suggest deriving a custom class here so that you can get at the code. Then have each nav-bar controller reside within that tab-bar (within the nib-structure), but only worry about adding them when they are displayed.
Using Interface Builder: It was pretty simple to do with IB. In my MainWindow.xib file, the top level has all the normal stuff, Window, the generic UITabBarController and the UIViewControllers that I wish to push onto each UINavigationController, we'll say UIViewController 1b and 2b (1a and 2a are the two UIViewControllers that are the default views for each respective navbar). Nested within the UITabBarController, I have the UITabBar and my two UINavigationControllers. Within each, I have a UINavigationBar, a UIViewController, and a UITabBarItem, in that order. Here's what my code looks like in the app delegate:
[window addSubview:tabBarController.view];
[tabBarController setSelectedIndex:0];
Then when I want to make use of the navbars I do this:
UIViewController* newView = [[UIViewController alloc]initWithNibName:#"newViewController" bundle:nil];
[self.navigationController pushViewController:newView animated:TRUE];
[newView release];
That's all it takes for me to get it to work (I might have forgotten some IB wiring).
My final thought is that the bundle might be messing with your navbars. I have never used it and don't know much about the pros and cons, but you might try killing that to see if it's a fix, at least temporarily.

UINavigationController template

When I open the new file dialog I see only 3 Cocoa Touch Classes: Objective-C class, Objective-C test case class, UIViewController subclass. When I last worked with Xcode, before I updated it, I remember there were more than 3 options, which included what I'm looking for: UINavigationController.
So where can I find this template to create a UINavigatonController subclass?
Apple's made a lot of changes to Xcode's file creation screen and they've caught me of guard as well. I'm not exactly sure what your navigation controller is attempting to accomplish, but this post at the iPhoneDevSDK forum explains why you shouldn't subclass UINavigationController and instead present your view modally.
iPhoneSDK Forum UINavigation Controller Subclassing
In addition to the discussion in the forum about modal view controllers, here is source code from Apple's View Controller programming guide that explains how accomplish this.
YourViewController *viewController = [[YourViewController alloc] initWithNibName:#"YourView" bundle:nil];
viewController.delegate = self;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[self presentModalViewController:navigationController animated:YES];
There are no required methods found in the header file protocols for UINavigationController. I'm unsure what the old template used to look like, but you could try re-creating one using one of your old classes, or just examine the header file for the methods you want to commonly implement and store your own template for it.