Adding a variable to all UIViewControllers - iphone

I have this class (let's call it FooViewController) that's a subclass of UIViewController. It's supposed to act similarly to a UINavigationController, in that there's a rootController and you can add other UIViewControllers to it. And each UIViewController within FooViewController can create another UIViewController and push that new UIViewController to FooViewController.
Here's an example of what I mean. This is the auto-created code when you add a new UITableViewController to your project.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"Nib name" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
Here's the question: How can I have a variable like navigationController in all of my UIViewControllers? Just like you can add methods to existing classes using categories, can you also add variables to existing classes? It sounds like Associative References is what I'm looking for, but it's only available on a Mac.
One solution that would work for me is to subclass all the UIViewControllers that I might use, and have my actual classes subclass off of those. For example, I might do:
#interface FooUIViewController : UIViewController {
FooViewController *fooViewController;
}
#interface FooUITableViewController : UITableViewController {
FooViewController *fooViewController;
}
So that I could do: [self.fooViewController pushViewController:detailViewController];. But this seems like a dirty way of doing it.
I feel this shouldn't be a difficult thing to do and maybe I'm thinking about it wrong. Any thoughts? Thanks!

No, you can't add an instance variable to UIViewController.
Subclassing is not a bad way of doing it, considering more things it can do in relation to FooViewController. It doesn't harm the design of your classes because they depend on FooViewController anyway (just as the same as UIViewController having navigationController property).
Another way of doing it is to access the FooViewController object via the application delegate. But I think this is a dirty way of doing it (because they now depend on your application delegate).

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.

own view controller template

here i want to ask a question regarding view controller template for iphone or ipad.
question: i want to design a view controller with some images and make it as common template and need to use it as a template to other view controller too. (also some buttons and labels are the inclusions if needed)
(ie) when ever i call that class, automatically its components should be inherited to calling class.
is it possible?
it would be grateful if anyone answers me the suitable or favourable
I don't know how adMob works but like everything else you can create a BaseViewController in which you can add your adMob(in the viewDidLoad method) and then all the other viewControllers can subClass this BaseViewController. just call [super viewDidLoad]; in the viewDidLoad methods of your viewControllers and you will have it...
hoping this sorts your problem... :) this is what I usually do.
Sounds like you just need to derive your view controllers from a common base class. Is there something that you want to do that's not provided by inheritance?
Yes it is possible.
You need to create #property for your components to re use them in other class
in class1:
#property(nonatomic,retain) NSMutableArray *exercisesArray;
in class2:
#property(nonatomic,retain) NSMutableArray *array;
nextViewController *start = [[nextViewController alloc]initWithNibName:#"nextViewController" bundle:nil];
next.array = exercisesArray

Pass a Variable Up the Navigation Stack? - iPhone

I am able to pass a variable forward from view controller to view controller by pushing its view controller onto the navigation stack. An example of how I do it would be this:
MyViewController *controller = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
controller.myString = stringToPass;
[self.navigationController pushViewController:controller animated:YES];
[controller release];
However, what do I do if I want to pass a variable BACK UP the navigation stack? Using popViewControllerAnimated rather than pushViewController does not pass the variable up like I thought it would.
I need to be able to access the variable several pops up from the view controller it is defined in.
Any help will be greatly appreciated :)
You're passing values, not variables.
A view controller should not be responsible for popping itself. With Apple's view controllers (e.g. UIImagePicker), it is the parent view controller's responsibility to do the popping; the parent VC can also obtain the current value. (Not entirely correct; it might access the value before a keyboard autocompletion is applied)
Alternatively, if it's a value that can be shared globally, you can store it in your application delegate.
You can get a hold of the navigation controller in the VC stack using self.navigationController. You can just call some method like
[self.navigationController setMyString:stringToPassUp];
There are several more ways, e.g. self.tabBarController for the tabbarcontroller up in the stack, or most simply
[self.parentViewController setMyString:stringToPassUp];
edit given the downvotes on the examples above, and nobody giving a better explanation, let's discuss the proper way to do this.
If you have some object (like your MyViewController *controller) and that object has something to tell you, the usual approach is this:
MyViewController gets a delegate property, of type (id)
the view controller instantiating the MyViewController, sets this delegate property, like so:
controller.delegate = self;
MyViewController will, when it has something to say, do something like:
[self.delegate delegateMessage:arg1]; to "pass the message up" as you put it.
To do it perfectly, you may want to create your own #protocol MyViewControllerDelegate, and declare the class which would be setting controller.delegate = self; to adopt this protocol, by putting <MyViewControllerDelegate> on the #interface line. The delegate property of MyViewController should then be declared id<MyViewControllerDelegate> delegate;, so that the [self.delegate ...] messages can be matched to the protocol specification.
Basically the whole Cocoa Touch API works like this. Just have a look around for ideas how to implement your interaction.

Subclass of UIViewController initialising itself from a .xib can't use UINavigationItem?

I've finally had to give up the relentless search for an answer to this question, as I just can't find anyone that's asked it before! So hope someone can provide some insight. I'll start by explaining what I can do, then compare that with what I can't figure out how to do.
Suppose I have a custom VC called RootViewController. It contains an outlet of type MyViewController. My RootViewController has an .xib which contains a generic VC object dragged out of IB's palette which is given the class type of MyViewController, I set the right bar button item to a UIBarButtonItem called 'Cancel'. The RootViewController unarchives it's .xib, hooks up the outlet to the MyViewController object, I push said object on to the navigation stack which displays as expected, I have a view and a button on the navigation bar that says 'Cancel'.
The problem I have with this approach is that I want MyViewController to be re-usable across any object that might want to create a MyViewController. This therefore means that the "File's Owner" could be any object. Previously it was RootViewController, but what if another object wanted to instantiate a MyViewController? The Files Owner will be different every time. I also want it to be able to completely initialise itself from a nicely self-contained .xib. Basically, I want to do this:
MyViewController *myVC = [[MyViewController alloc] init];
And in the implementation of MyViewController, I write this:
- (id)init
{
if ( self = [super initWithNibName:#"MyViewController" bundle:nil] )
{
// Initialisation
}
}
This neatly hides the name of the .xib used to intialise the VC, uses all the goodness IB gives me in configuring the controller and view, and with MyViewController always being the owner, it means I solve the File's Owner problem too - it will work for any type of class that creates it.
So, in order to achieve this, I create a .xib, set the File's Owner to be of type MyViewController, drop in a UINavigationItem and add the UIBarButtonItem. I now have a .xib structurally the same as before, except it does not use IB's generic VC object as separate top level object, the .xib is the VC definition, rather than something that contains a VC definition.
So, given that File's Owner is a MyViewController and as such a subclass of UIViewController (supposedly inheriting everything you get by using a UIViewController from IB's palette), then I should inherit all the functionality of it's superclass... Except it doesn't.
When the .xib in unarchived, it does not hook up the UINavigationItem. Therefore, when it's pushed on to the navigation stack, none of the bar button items are displayed.
UIViewControllers navigationItem property is read-only, creating an outlet for it in iPhone OS 3.0 is therefore deprecated.
So, at the end of all this, how on earth does the nib loading code manage to connect IB's version of the VC object to the navigation item? And why, even though my object is a UIViewController through inheritance, will it not do it for my object? I am completely at a loss to fathom this out...
Thanks for reading this far! Hope to hear from you guru's
Only a UINavigationController sets UIViewController's navigationItem property. Do something like this:
MyViewController *viewController = [[MyViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
Assuming you are in a view controller that already has a navigation controller. After you have pushed it, you will be able to access navigationItem from inside MyViewController.
Right, I think I get it now. I thought I'd post an answer to my own question as I've seen at least one other person ask a similar question, and both seem to point to the same conclusion, so I hope this will be of help to others.
Essentially (and this will sound obvious) a UIViewController is not a Proxy Object! It's not instantly obvious as we're all used to the idea that if any two objects inherit from the same base class, then their implementations are the same and they will behave in exactly the same way (assuming no customisation in the inheriting class). But an IB object's type is distinct from the class attribute you can assign to these objects.
They are not both UIViewController objects because their class attribute it set in such a way.
Simply setting the class attribute of these objects in IB does not mean that these objects are now UIViewController objects. A View Controller object remains a View Controler object, and a Proxy Object remains a Proxy Object. As far as IB is concerned, they are both very different beasts, they just happen to have the same class attribute.
Just take a look in to the .xib and you'll find your IB View Controller objects have been archived like this:
<object class="IBUIViewController" id="...">
...
</object>
And the Proxy Object (that is set to a subclass of a UIViewController) is archived like this:
<object class="IBProxyObject" id="...">
...
</object>
As you can see they are both very different types, one is a IBUIViewController and the other is a IBProxyObject - and then it starts to makes sense - you can't impose VC controller attributes on an object of type IBProxyObject.
It is also interesting to note the class type is an IBUIViewController object and not just a UIViewController object. Whether this is just a naming convention or not I don't know, but it could also imply that IB's view controller objects wrap the instantiated VC object, or is a factory object for it. For example, you can set a "Resize View From NIB" attribute in IB's view controller object - but I can find no equivalent property or methods in the UIViewController reference docs.
So in conclusion, if you're trying to instantiate an object programatically instead of using outlets to an IB object, be prepared to implement some of the initialisation that the IB version would otherwise provide for you...

What's the best way to create grouped table view controllers on the iphone?

UITableViewController has an initWithStyle method where you pass it the style you want the table view to use, plain or grouped. These means that to create and use my table view controller, somewhere else in the code, possibly in multiple places, I do this:
MyViewController *myViewController = [[MyViewController alloc] initWithStyle: UITableViewStyleGrouped];
[self.navigationController pushViewController: myViewController animated: YES];
[myViewController release];
These seems backwards to me. The classes that use my UITableViewController should not know or care if it used grouped or plain style, that is a concern that the MyViewController should take care of. What's the idiomatic way of dealing with this? Override the init method in MyViewController to call initWithStyle?
My answer as a newbie is 'just do it' and worry about the philosophical design issues later. All the examples I've seen, including popular books have that 'pattern' so it can't be too wrong.
Just be careful when overriding one init method on a superclass that calls another. I'm not sure about it here, but IF initWithStyle allready calls init in the superclass when you override init in the subclass and call the super you might have a loop on your hands.
I would just create a table view controller through xcode, assign the table the grouped style in Interface Builder, then override the init method if necessary:
- (id)init {
self = [super initWithNibName:#"MyViewControllerNibFileName" bundle:nil];
if (self) {
// Do other customisation
}
return self;
}
I needed to explicitly set the nib name for some reason, but sometimes the view controller just finds it automagically (I'm still a newbie, so not sure why this happens)