Using delegate with tabBarController on Storyboard - iphone

Would appreciate any advice as I try and wrap my head around this - I'm not sure if I'm implementing this wrong, or am working from the wrong premise (or both).
If I have a class in which I've created a protocol (the delegatOR) - in order to assign a delegate for that protocol, am I right to say I need to alloc/init the 'delegatee' class somewhere in the delegator's implementation file, and then assign it as the delegator's delegate?
If so, follow up question: I have a tabBarController set up in Storyboard, and when the user clicks on the 'end' tab I'd like to send a message to the viewController for the view they're about to leave, so it pops up an alert saying something like 'are you sure?'. Since storyboard does the initializing and allocating behind the scenes, I'm at a loss as to how to set up the delegate. I read in another posting about using the prepareForSegue method when segueing between two viewControllers to set the delegate, but can't work out a similar catch-and-set technique for the tabBarController.

If I have a class in which I've created a protocol (the delegatOR) - in order to assign a delegate for that protocol, am I right to say I need to alloc/init the 'delegatee' class somewhere in the delegator's implementation file, and then assign it as the delegator's delegate?
No. The way it usually works is that some other class, usually some kind of controller, will instantiate (i.e. alloc/init) an object as well as its delegate, and set the object's delegate.
For example, if you have a UITableView and a UITableViewDelegate, typically you have some kind of view controller that owns the UITableView, and also owns the UITableViewDelegate (often the view controller itself is the delegate). It's the view controller's job to set the table view's delegate. It most certainly is not the UITableView's job to create and set its delegate.
Still, you're right that setting delegates for view controllers can be hard when you're using Storyboard. If the prepareForSegue: method isn't working for you, I assume it's because the tab bar controller is your root view controller (as it well should be, according to the HIG). Since your app delegate will have a window property, you should be able to get to the root view controller from it:
UITabBarController *tabController = (UITabBarController *)self.window.rootViewController;
tabController.selectedIndex = [defaults integerForKey:kOptionLastTabSelectedKey];
tabController.delegate = self;

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:

IPhone : View property on a Xib, is it special?

I can't believe I am stuck on this but here goes.
I have a viewController which I am trying to add to another viewController via the addsubview property (basic as) however I am not doing viewController.view but viewController.myView1.
I basically have 2 root views on the xib (both with outlets) however I don't want to wire up the view property of the xib because I want to choose which view to show.
I can't get it to appear! Is the view property some sort of special hook?
I can't see why addsubview:mysubview should not work?
Some examples seem to use 2 different xib's but that seems a overkill
Firstly, Apple say a viewcontroller should be used for a whole screen full of views - (but I guess iPad container views are different).
I guess you're wanting to only use one of the viewcontrollers at once. In which case you shouldn't be using subview - just create the viewcontroller programmatically, create the view, assign the view to the viewcontroller's view property and then add the viewcontroller to the window or navigation bar.
and, in your viewcontroller:
self = [super initWithNibName:nibOne bundle:nil];
might be what you want - it allows you to use multiple nibs with a single viewcontroller.
Can you NSLog your viewController.myView1 before add it as a subview? Is it nil or not?
If it's nil, try to add this line before your addSubview: method:
[viewController view];
Note: The viewController I am talking about is the same viewController which have two root views view and myView1.
Your code should be good as this is fairly simple and I do it all the time.
Just make sure your outlets are wired properly and that you are synthesizing them and that they are public in your header.

What is the purpose of using UIViewController IBOutlet

I am in the learning phase of iOS programming. I am trying to add a NIB (called B) in another NIB (called A). To do so, I have added a View Controller in A which uses the B NIB (using NIB Name). In addition, in the A's ViewController, I have created an IBOutlet to store B's ViewController. Finally, in A I have connected the IBOutlet to the ViewController referring to B.
Now, I expect that when I run the project, I would see that B is loaded whenever A is loaded, but that is not the case. To achieve this, I have to initialize B pragmatically using initWithNibName in A's viewDidLoad method, and set it to the IBOutlet. But this part is something that is not dependent of using both IBOutlet in A and adding a ViewController to A that refers to B. In other words, if I had simply initialized B in A's viewDidLoad without creating an IBOutlet in A and without creating a View Controller object in A, that would have worked just as well.
So my question is, what is the purpose of using IBOutlet for adding custom View Controller NIBs?
Just because you have outlet to VC B in VC A, that does not mean view of VC B will be loaded automatically. If you check VC B outlet in VC A viewDidLoad, you'll see there is address to VC B, therefore NIB is loaded. To load its view, you have to access VCB.view and call appropriate methods in VCB like this:
[self.bViewController setFrame:[self frame]];
[self.bViewController viewDidLoad];
[self.bViewController viewWillAppear:NO];
[self.view addSubview:self.bViewController.view];
[self.bViewController viewDidAppear:NO];
You dont need to do all that, and adding another viewcontroller view in currently displayed view controller view hierarchy causes problems like interface rotation or touch events do not delivered properly.
You can use uiviewcontroller's presentViewController:animated:completion: method to load bViewController's view hierarchy and all the methods like viewDidAppear, viewDidLoad, viewWillAppear will be called automatically you do not need to call them manually.
You will find this article Abusing UIViewControllers useful.

Using an external UITableViewDelegate and calling back to a UINavigation controller through a UIViewController

I have UINavigation Controller that has a UIViewController pushed onto the stack and displayed.
That UIViewController has a UITableView and uses an external class for that UITableView's datasource and delegate. I do this because I need to swap that datasource dynamically. Everything works beautifully for displaying data.
However, when handling didSelectRowAtIndexPath "down" in the delegate
I'm struggling with the proper way to call all the way back to the UINavigationController. Whereas I'm used to being able to do something like this when the UIViewController and Delegates are the same object:
ExampleViewController *newViewController = [[ExampleViewController alloc] init];
[self.navigationController pushViewController:newViewController animated:YES];
[newViewController release];
I cannot do so from the delegate and I'm just not getting what the correct reference back to the UINavigationController should be is the pushViewController.
Do I need to set an ID similar to a delegate so that the UITableViewDelegate has a pointer directly back to the UINavigationController?
Only UIViewController instances that have been pushed onto a navigation stack have convenient access to a corresponding UINavigationControllerĀ instance through the "navigationController" property. So you have a couple of options:
1) Pass a reference to the navigation controller to your table view delegate(s) so they can call the "push" method you're used to using.
2) Pass a reference to your view controller to your table view delegate(s), and have them call a method when a user taps a table view row. You can write your own delegate protocol and implement it in your view controller to formalize this approach.

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.