What is Root View Controller - objective-c

When we do things like
[self.window addSubview:switchViewController.view];
Are we setting switchViewController as the rootViewController?

Actually no. rootViewController is a main controller. Callbacks such as didReceiveMemoryWarning will be sent to rootViewController only. And it must decide to whom it is addressed.
A window contain any amount of views. Some of them have UIViewControllers, some don't. So [self.window addSubview:switchViewController.view]; just add a view to a window, it doesn't know anything about its controller. The controllers should be manipulated by a rootViewController. The only way to set switchViewController as the rootViewController is to invoke something like self.rootViewController = switchViewController; in your AppDelegate file.

Related

How to override the UIBarButtonItem triggered segue defined in a storyboard

I have a UIViewController in a storyboard iPhone app that I need to reuse in several places. In the storyboard, there is a UIBarButtonItem that has a triggered segue to another controller. In some cases, however, I would like to override the behavior of the button to segue to a different view controller. I'm thinking that there must be a way to either initialize the view controller with a message that specifies the target view controller, or to set some property after the controller is initialized but before it's pushed?
One challenge seems to be that segues can't be defined programmatically (based on what I've read so far), and I don't think I can have multiple segues on the same view controller in storyboard. So I may need to do something like this:
[self presentModalViewController:myNewVC animated:YES];
... rather than use a segue, but I'm not sure how to override the behavior of the button defined in storyboard to do it that way.
Any help is appreciated.
Just create an IBAction and a BOOL for some condition to pick which view controller should be instantiated.
UIViewController *viewController;
if (someCondition) {
viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"someViewID"];
}else{
viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"someOtherID"];
}
[self presentViewController:viewController animated:YES completion:nil];

Accessing an instance method of a ViewController From Another view controller

Let's say I have a view controller called vc1, which a synthesized property called property1, and i wants to access it from another view controller (vc2) and change it from vc2.
Now the methods created by the #syntisize to change and get properties are instance methods, so how can I get to them fro another view controller (do view controllers have instances in the app, and if so, what are they?)
Just to be clear I am using storyboards, so I never really instantiate the view controllers...
VC1.m:
-(void) yourMethod {
...
}
VC2.m
YOURViewController * vc2 = [[YOURViewController alloc]init];
[vc yourMethod];
[vc release];
Make sure to import your YOURViewController in your other view .m file
Something like that should work.
Or if you're having problems, try this tutorial here:
Tutorial on How-To Pass Data Between Two View Controllers
Hope this helps :)
While you can do it the way you describe, I think the common technique (assuming VC1 has a segue to VC2) is a bit different, where VC2 will have a property that will be set by prepareForSegue. See Configuring the Destination Controller When a Segue is Triggered in the View Controller Programming Guide.
You will need to link the storyboard views with the viewcontrollers so the view for vc1 would use the class vc1 etc for the rest (I assume you have done this because this is important when coding for different views)
Then all you need to do is where ever you are calling the properties so lets say the viewDidLoad method, declare the view controller like this:
- (void) viewDidLoad {
vc1 *viewController;
// Now you change the variable I'll presume its a UILabel so I'll change its text [viewController.property1 setText:#"I changed a different views UILabel"];
}
Let me know whether this works... Its worked for me before so should work

Custom back button UINavigationController across my entire app

I am looking to replace the back button in the UINavigationController throughout my application. My requirements is that this back button be defined in one XIB and if possible, the code to set it is in one place.
I have seen various methods that set the property self.navigationItem.backBarButtonItem to be a UIBarButtomItem with the custom button as it's view, e.g. [[UIBarButtonItem alloc] initWithCustomView:myButton];
My first thought was to create a global category (not sure if that's the term, I'm new to Objective-C as you might have guessed) that implements 'ViewDidLoad' for all my UINavigationControllers, and setting this property. My problem is loading the XIB to this button that I create at runtime.
Does anyone have a suggestion on a neat way of doing this (I guess it must be a common thing to do, and I can't imagine repeating code in all my screens). I have considered creating a UINavigationController subclass, however I wasn't sure how this would effect my custom implementations of ViewDidLoad.
Any advice much appreciated. Also I need to target >= iOS4 (the appearance API is iOS5 only).
I prefer to not force inheritance where possible so you could do this with two categories
#interface UIViewController (backButtonAdditions)
- (void)ps_addBackbutton;
#end
#implementation UIViewController (backButtonAdditions)
- (void)ps_addBackbutton;
{
// add back button
}
#end
#interface UINavigationController (backButtonAdditions)
- (void)ps_pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
#end
#implementation UINavigationController (backButtonAdditions)
- (void)ps_pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
{
[viewController ps_addBackbutton];
[self pushViewController:viewController animated:animated];
}
#end
Now #import these files as appropriate and instead of using
[self.navigationController pushViewController:aViewController YES];
use
[self.navigationController ps_pushViewController:aViewController YES];
Disclaimer
I free styled this in the browser so you may need to tweak it
I had the same issue in my current project, the solution I came up with was to create a MYBaseViewController base class without xib and there in viewDidLoad programmatically (if you want to init barButtonItem with custom view, you are not able to create in xib anyways) create a customBackButton (and of course release it and set to nil viewDidUnload.
This works good for me because this way I can create xibs for all my other viewControllers that are subclasses of MYBaseViewController(if you created a view for base class in nib you would not be able to create a nib for a subclass).

ObjC/iPad buttons in subviews calling methods in master controller. How?

Is my model all a mess?
I'm writing an iPad app with multiple views. I have a masterViewController that subviews other view controllers. Some of these subviews contain buttons.
The user clicks these buttons to change views. It is my intention that the masterViewController be responsible for changing these views. But how?
How can the masterViewController tell when the button is pressed?
#implementation subViewControllerOne
- (IBAction) switchToSubViewTwo {
//Where to I send my message?
[masterViewController myMethod] //doesn't work because I need to message the specific instance
}
You could
Pass a reference to your master controller to the sub controllers and tell it that way
Make your master controller a singleton and reference it that way
You could have your sub controllers post an NSNotification that the master listens for.
You might want to go by the AppDelegate to find a reference to your controller.
Make sure you add an outlet for your masterViewController in the AppDelegate and connect it (using Interface Builder). Then, in your subview's controller, get a reference to your AppDelegate and run your great method, like so:
MyAppDelegate *delegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
[delegate myMethod];

Cocoa-Touch internals: How does the view know its controller?

If you add a subview to a view or add a view to a window, how does iOS know which controller this view belongs too?
Easy example:
Have a UIView without UIViewController and add it to the window [window addSubView:myView] --> it will not rotate.
Now use a UIViewController, have it implement shouldAutoRotateToInterfaceOrientation: and add the controller's view to the window: [window addSubView:myController.view] --> magically, the view will adjust to interface orientation.
But look at the code: in both cases a UIView was added. How can iOS possibly be aware that in the second case a UIViewController was involved?
I'm interested in how this is done internally. My best guess is that UIViewController.view is a setter which adds the controller to an internal array of controllers or assigns itself to some internal variable which holds the currently active controller.
Simple. Look in UIView.h. It's right there. Each UIView has a pointer back to a UIViewController (which is apparently referred to as the "viewDelegate").
Dave DeLong is correct (and gets +1) as it is clearly defined UIView.h as #package so anything in UIKit can access it.
Here is an example of accessing that variable for educational purposes only (obviously you will not do this in a real application).
SomeAppDelegate.m
#synthesize navigationController=_navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
//DO NOT TRY THIS AT HOME
UIView *mynavview = self.navigationController.view;
//Guaranteed _viewDelegate atleast in iOS 4.3
Ivar ivar = class_getInstanceVariable([UIView class], "_viewDelegate");
UIViewController *controller = object_getIvar(mynavview, ivar);
NSLog(#"controller = self.navigationController? %#", controller == self.navigationController ? #"Yes" : #"No");
return YES;
}
UIViewController has a private class method (called controllerForView:, I believe) which is used to find the view's controller. Internally, there is probably a table used to connect the two together, and this method simply finds the proper location in that table and returns its value. When the result is nil, the default implementation will be used (don't rotate).
If you want to be sure about the name of the method, set a breakpoint in -[UIView becomeFirstResponder], tap on a text field, and step through the code until it shows up in the call stack. I suggest using becomeFirstResponder because it is easier to control than most things which get the view controller.