(iPhone,Obj-C) Where does this "view" property come from? - iphone

I ran across this segment of code (in .m implementation file in an IBAction instance method):
UIActionSheet *actionSheet = [[UIActionSheet alloc] ...];
...
[actionSheet showInView:self.view];
self refers to the button but I wanted to know where I could find out about this "view" property since I looked up UIButton, UIView, NSObject, but could not find out where this "view" property came from.
(also, any corrections on terminology would be appreciated since I am trying to learn all the correct terms - I'm a beginner iPhone programmer)

The view property is one of a UIViewController. The controller usually creates a view in its initialization, and is responsible for managing it. Any subclass of UIViewController will inherit this property. See this link for more details. Also, you may want to familiarize yourself with MVC(model view controller), as this might make it more clear why the controller has a view property. See this link for more details.

Related

Still fuzzy on creating objects in Code, IB, and combined

I'm still quite new to Xcode etc, so Im a bit fuzzy when it comes do creating/using objects in Xcode.
For example, I used a tutorial for creating a GestureRecognizer in code - it's pretty simple.
Right now I have only this in my .h:
#interface PlayPage : UIViewController <UIGestureRecognizerDelegate>
And this in my .m:
UITapGestureRecognizer *tapTwo=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTap)];
[tapTwo setDelegate:self];
[tapTwo setNumberOfTapsRequired:2];
[tapTwo setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:tapTwo]; //adds it to view
Here are my question(s):
1) If I also want to see the GestureRecognizer in IB along with the above code (perhaps so I can change some settings since I'm not familiar with everything) how do I accomplish that?
2) How would you decide when to use all code vs. a mix of code/IB ? Is it strictly a matter of style/comfort/familiarity?
3) It seems declaring different objects requires different 'setup' code (UITextLabel vs GestureRecognizer vs UITextField,for example) - how do you know when to use which code?
Thanks for helping to clear this up.
In Xcode 4.x Interface builder there are UIGestureRecognizer objects that you can drag onto your views. So you would need to add the UIGestureRecognizer in Interface builder (if you need to do any more config in code then connect them up with an outlet as well).
I try and do anything I can in IB and drop to code when I have to but it is a matter of preference.
I'd rather eyeball the position of views and use the snap guides than spend a while figuring it out in code.
You can hide a lot of boiler plate code if you do it in IB
Start typing and look at the autocompletion options to jog your memory. But more importantly every time you use a class you have not used before you should at least skim the documentation to see what is suggested. Half of the questions on this site could easily be solved if the question poster actually bothered to look at the vast documentation available.
Update
You need to create the UIGestureRecognizer in Interface Builder not your code.
In Interface Builder ensure that the Utilities pain is visible and drag and drop a UIGestureRecognizer of the required class onto your view
This is what the UIGestureRecognizer class look like:
You then configure the object in the Utilities panel. If you want to be able to access this object in your code then you will need an IBOutlet.
In your classes header file add a new ivar for this
#property (nonatomic, retain) IBOutlet UITapGestureRecognizer *tapGestureRecognizer;
In the implementation file synthesize this
#synthesize tapGestureRecognizer = _tapGestureRecognizer;
Then back in Interface builder connect this up by ctrl + click + dragging from the File's Owner to the UIGestureRecognizer subclass. Then selecting your ivar from the window that appears:

Passing info to a delegate from a UIActionSheet

My understanding about passing data back to a delegate completely revolves around making a new view controller and having it conform to a protocol.
I am trying to get a time input back from a UIDatePicker set with the UIDatePickerModeCountDownTimer mode and I am running into issues.
Inside of Main1.m I create a UIActionSheet *action, and set everything up so that it presents itself with a UIDatePicker on a click. Inside of Main.m I also say:
main.delegate = self;
If this were not a UIActionSheet, I would make a protocol reference inside the new ViewController and then have the new vc pass data to a method that Main has, but I can't do that with a UIActionSheet!!
What am I missing? I assume there is something inherently different about Action Sheets, ut I can't figure it out.
Thanks for your help!!!
EDIT:
Sorry! I was giving a generic name to my view controller. It isn't actually Main.m, its FirstViewController.h/m
I didn't realize that my generic reference was getting mixed up with the Main.m file that is completely different than a vc.
I don't exactly understand why you're putting your delegate assignment in Main.m. I assume that you're setting up your UIActionSheet in a ViewController, and launching it from there. In this case, your ViewController is your delegate object. So you need to make sure that your ViewController implements the UIActionSheetDelegate. ie:
#interface SomeController : UIViewController <UIActionSheetDelegate>
Then you simply implement the required methods of that delegate in your view controller class, and that should do it. If I'm missing something about how you're implementing this, then you need to provide more code examples to examine.

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

UIViewController created with initWithNibName: bundle: or via an IBOutlet behave differently

I found a strange behavior, and would like to be explained what assertion I am making that is wrong.
In an AppDelegate class of a freshly created WindowBased project, I am adding a UIViewController to the window.
I can do it two different ways:
- with an IBOutlet. In IB, I simply instanced an UIViewController, set its class to TestViewController and connected it (scenario A of the code).
- creating the UIViewController with code (scenario B).
- (void)applicationDidFinishLaunching:(UIApplication *)application {
#define USE_IBOUTLET YES // Comment this line to switch to scenario B
#ifdef USE_IBOUTLET
// Scenario A
[window addSubview:theTestViewController.view];
[window makeKeyAndVisible];
#endif
#ifndef USE_IBOUTLET
// Scenario B
TestViewController *theTestViewControllerProgrammatically;
theTestViewControllerProgrammatically = [[TestViewController alloc] initWithNibName:nil bundle:nil];
// According to Apple: "It is a good idea to set the view's frame before adding it to a window.", so let's do it
[theTestViewControllerProgrammatically.view setFrame:[[UIScreen mainScreen] applicationFrame]];
[window addSubview:theTestViewControllerProgrammatically.view];
[window makeKeyAndVisible];
#endif
}
As I did not do any customization of the object in IB, I should have the same behavior in both scenario.
Scenario A, using the IBOutlet works as expected.
But the scenario B has the following problems:
- The view is not at the right position (20 pixels to high, and covered by the status bar).
- The view doesn't resize properly (for example, try to toggle the In Call Status bar)
Why?
Zip archive of the project here if you want to reproduce the problem: http://dl.dropbox.com/u/1899122/code/ProtoWindowBasedStrangeness.zip
This is going to sound really silly after my long-winded answers, but the problem you're having is simple to fix (programatically).
This line:
[theTestViewController.view setFrame:[[UIScreen mainScreen] applicationFrame]];
Should actually be:
[theTestViewControllerProgrammaticaly setFrame:[[UIScreen mainScreen] applicationFrame]];
You were setting the frame for the VC set by IB, not by the one you created programatically.
Anyway - it's worth noting that all my comments still apply! There are still a few things you'll have to do programmatically if you don't use IB's controller objects (for example, setting up the navigation bar items)
Paul
I have been having a very similar problem to you in that I noticed VC objects are not all created equal! The problem I'm having is setting the navigation bar items, I just can't seem to do it when File's Owner is a view controller object that I instantiate programatically. It only works if I unarchive IB's controller objects.
I downloaded your project and had a play around with it, and it got me thinking some more about what might be going on. I think I can provide a reasonable answer, but not sure if there is a simple solution...
What I believe is going on is that Apple have created these controller objects in IB that are slightly more specialised. One suggestion this might be true is that IB VC objects have an attribute you can set that has no direct corresponding property for a UIViewController class that I can see, so IB's controller objects may have some additional functionality that non-IB UIViewController subclasses can't take advantage of. Given that objects in an .xib are complete 'freeze-dried' objects, Apple may have included all kinds of private attributes we can't see or use in their IB versions of them - this may have some effect on how the objects are initialised.
For example, in your MainWindow.xib, select the IB VC object and you can set attributes on it from the Inspector Palette, such as "Resize View From NIB". If you un-check this and re-run your app, you'll see the VC appear exactly as it does in scenario B. As you can't check this item when from the File's Owner attributes (even though it is as a UIViewController), you're unable to take advantage of whatever is being done by the view controller to give you the behaviour you want.
The result of this is that when you use TestViewController.xib to initialise your VC object in code, none of the IB specific attributes of a VC are set, therefore a bog-standard UIViewController is created, and so things like the "Resize View From NIB" attribute and setting up the navigation items have to be implemented yourself.
I've not yet found a way to take advantage of the functionality that IB's view controllers have when I instantiate them using initWithNibName:bundle:nibBundle (I'm guessing it's all private stuff we can't access), but hopefully this might have given you a starting point...
Of course, I could be completely wrong and someone will make me look like a complete idiot!
Paul
Probably in case B that view is not aware of the presence of a status bar. You need to resize it accordingly and adjust its position to take the status bar into account. That is done by changing the frame (size) and bounds (location) properties of a UIView.

What's the right way to add a ToolBar to a UITableView?

I'm writing a Navigation-Based iPhone app, and I'd like to have a UIToolBar docked at the bottom of my screen, with a UITableView scrolling between the tool bar and the navigation bar.
I've seen a couple of forums where it's been suggested that the View Controller handling this view should be a standard UIViewController rather than a UITableViewController. The view controller would have to implement the UITableView delegate and data source methods in addition to all of the standard UIViewController overrides. What (if any) built-in functionality do I need to recreate in this view controller subclass other than the aforementioned protocols to have it act like a UITableViewController? Is there anything I'm losing by going this route?
Or would it be better to nest a UITableViewController inside of a standard UIViewController?
As of OS 3.0 the Navigation Controller has a tool bar built in. To make it appear:
[self.navigationController setToolbarHidden:NO];
By implmenting:
- (void)setToolbarItems:(NSArray *)toolbarItems animated:(BOOL)animated
in your view controller, you can configure the items of the tool bar.
So you no longer have to worry about where the tool bar is located in your hierarchy.
(corrected typo)
Corey Floyd is mostly correct, except that
[self.navigationController setToolBarHidden:NO];
should be
[self.navigationController setToolbarHidden:NO];
That is, the "b" in "setToolbarHidden" must be lowercase. Also, method name listed in the iPhone OS Reference is actually
- (void)setToolbarHidden:(BOOL)hidden animated:(BOOL)animated
though it seems that omitting the animated parameter works too.
//Tool bar
[self.navigationController setToolbarHidden:NO];
UIBarButtonItem *buttonItem = [[ UIBarButtonItem alloc ] initWithTitle: #"Select All"
style: UIBarButtonItemStyleBordered
target: self
action: #selector(selectAll:) ];
UIBarButtonItem *buttonNext = [[UIBarButtonItem alloc]initWithTitle:#"Next" style:UIBarButtonItemStyleBordered target:self action:#selector(goNext:)];
self.toolbarItems = [ NSArray arrayWithObjects: buttonItem, buttonNext, nil ];
[ buttonItem release ];
[buttonNext release];
All you need do is implement the UITableViewDelegate and UITableViewDatasource methods required for the level of table view functionality you require. These methods can be in any class(es) though said classes should conform to the relevant protocols. The delegate and datasource should be set on the UITableView instance - either programatically or with Interface Builder. According to the docs you will lose some functionality - see the overview section.
Personally I find that many developers seem to be obsessed with providing all of this functionality in a single monolithic view controller class, and that because they have a table view in their view then a subclass of UITableViewController must be used. However, I like to consider the Single Responsibility Principle and will often break the datasource and delegate into separate classes when the complexity is anything other than simple. The code is also then not tied to a specific UIViewController implementation.
In situations where I have separate datasource/delegate classes I often construct and wire them up to the table view using Interface Builder and not in code. This approach (to me at least) is in the spirit of Dependency Injection and saves writing some boiler-plate code, and provides some level of decoupling.
These choices of course are influenced by the complexity of the functionality that you are trying to achieve - for simple implementations I might find myself using UITableViewController.
Try out this:
self.navigationController.toolbarHidden = NO;
Hope it helps you.