Archive for May, 2009

Presence 1

Thursday, May 28th, 2009

We need create an app that subclasses the UIViewController class. What is supposed to happen essentially is that we should hit a button and navigate to another window. Eventually multiple buttons will take us to different windows. I was initially feeling inspired by my new found confidence in my understanding of InterfaceBuilder. That all came crashing down when I tried applying some new concepts (loading views dynamically). It’s really not all that complex, just more so than I was initially prepared for.

Starting with my applicationDidFinishLaunching method (in my delegate):

// init the navigation controller (don’t mistake for a view)
navigationController = [[UINavigationController alloc] init];

// instantiate the first view controller (bottom of the stack)
PersonListViewController *listViewController = [[PersonListViewController alloc] initWithNibName:@”PersonListView” bundle:nil];

// push the view controller to the navigation controller
[navigationController pushViewController:listViewController animated:NO];

// the navigation controller now owns the view, let the copy go here
[listViewController release];

// add the navigation controller to the display
[window addSubview:navigationController.view];

// Override point for customization after application launch
[window makeKeyAndVisible];

I’m initializing my navigation controller here first (as opposed to doing it in the nib). Then I init my first view controller. This is going to be the default view for my navigation controller and is going to live on the bottom of the view stack (first on, last off).

Once I’ve pushed my PersonListView to my UINavigationController I’m done with it and can release.

From there I just need to add navigationController to my window and make it visible.

Adding another view

What I need to remember here is that this is calling it’s own nib (initWithNibName). I need to create this nib file, then I need to specify my PersonListViewController class as the File’s Owner, otherwise I get the following error (when trying to load the nib I called PersonListView:

loaded the “PersonListView” nib but the view outlet was not set.

The File’s Owner is an object proxy. The reason for the File’s Owner (as I have come to understand it after reading this thread) is basically to let objects that are currently loaded into your app know about objects that haven’t quite loaded yet so that objects your existing nib have a reference to those pre-existing objects.

Maybe this is a bit of a stretch, but I liken it to the english word “thing” (bear with me). I often use “thing” when I’m unable to immediately recall the actual name of the object I’m referring to. “Thing” is a placeholder while I flesh out the rest of my sentence.

Simply designating my PersonListViewController as the File’s Owner doesn’t get rid of this error though. I need to connect the view object in my nib to the view outlet in my UIViewController subclass (PersonListViewController).

With that figured out I can move on to the navigation part of the assignment. In my PersonListView nib I create a button that I’ll use to trigger the move to the next view in the stack, and the action that handles the button click.
Then I create a new class that subclasses the UIViewController (just like my PersonListViewController) and call it PersonDetailViewController. When the button in PersonListView is clicked I add this view to the stack by pushing the new view to the navigationController:

PersonDetailViewController *personDetail = [[PersonDetailViewController alloc] initWithNibName:@”PersonDetail” bundle:nil];
[[self navigationController] pushViewController:personDetail animated:YES];

A few things to note:

  • Dynamically loading and displaying an image:
    This was interesting. I initially concocted this (the uImage value was set to “mug.jpg” which exists in my main bundle):

    NSData *imgData = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@", [[people objectAtIndex:user] uImage]]];
    UIImage *diasplayImage = [UIImage imageWithData:imgData];
    uImage.image = diasplayImage;

    I couldn’t figure out why it didn’t work, until I read more of the documentation. the dataWithContentsOfFile method in NSData expects an absolute path to the image. As soon as I gave it “/Users/me/absolute/path/to/mug.jpg” it worked like a charm. I’m going to have to learn more about the iPhone filesystem, but for this app, loading an image this way isn’t really what I want. I’d rather just grab the image relative to my main bundle. I do this like so:

    uImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@",[[people objectAtIndex:user] uImage]]];

    imageNamed looks first in the cache for the image. If it doesn’t find it there it will look in my main bundle, cache it, then display it.

  • Don’t forget to link the UIImageView object (in the nib) to my “File’s Owner” uImage outlet.
  • I’m using the tag property of each button in my PersonListViewController to tell my PersonDetailViewController which button was pressed (which user was selected)

Assignment 3

Tuesday, May 26th, 2009

I was having a very hard time with this one. My problem was that I still didn’t fully understand the relationship between XCode and Interface Builder. I’m not saying I know it all now, but I think I’m closer.

I had been struggling with the seeming lack of instantiation for my controller. I couldn’t find anywhere in my generated code (i.e. code created after dragging an object into my nib file window, setting up outlets, actions, etc, and selecting File>Write Class Files) where my Controller class was being instantiated. I wanted to assume that the nib takes care of this, well, it does, or, I do. By virtue of dragging the object into my nib window and assigning a class to it I am instantiating that class (it’s all spelled out for me right there in the description of the object, I missed it initially).

This helps to allow connections to make more sense to me. Once I got past the hocus pocus of magical object instantiation, I could look at connections more simply, as just connections, not some nebulous directive to instantiate as well. This had me thinking though. I was initializing my PolygonView class in my controller’s init method. It didn’t seem in keeping with my theory that interface builder takes care of instantiation, so I pulled the polygon init code out of my controller class to see how far south things would go. My code compiled without a problem, and my app ran just as well as before, except better, because now I wasn’t wasting memory instantiating an unused object.

My controller class keeps track of my view(s) and model(s). Within my controller I set up instance vars. I use interface builder to point them to the objects they represent.

Just to get this to sink in a bit more I played around with multiple instances of my PolygonView class. Adding a couple more poly view objects for a preview of the next and previous polygons.

Assignment 2B – HelloPolly (Part I)

Sunday, May 24th, 2009

I’m revisiting this assignment and I think I originally blocked out most of what the walkthrough was trying to explain. At this point there is still a whole lot for me to learn about IB, but after getting as far as assignment 8 and still being confused by IB’s role this was a good refresher.

InterfaceBuilder can write class files for you based on actions, outlets and connections you specify. Though the connections don’t ever seem to be apparent in the code (at least not in a way that I could term as meaningful). This has proven to represent the biggest challenge for me. I like to see everything that’s happening in my code.

I need this to sink in.

Let’s say I have two buttons and a label in my window, setting up a controller class goes like this:

  • Drag an Object to my nib file window
  • Select the object and name the class whatever I want my controller to be called
  • Add some outlets that correspond with my two buttons and label, and actions as needed (so that my buttons will do something when tapped)
  • Connect the controller outlets to the UI elements in the window
  • Point the buttons to their respective actions like this:
    • Select the button of interest
    • in the connections inspector select whatever action I like (ex. touchUpInside)
    • Drag the action (using the connection circle to the right of the action) to my controller object in the nib window and let go
    • Select the appropriate action method in the controller (action methods will appear in a pop-up)

This is pretty much it. The walkthrough for the assignment goes into quite a bit of detail and more or less does it all for me. Just remember, create the controller object in IB and write class files from there. Link it up to the model class(es) by assigning my models to their own objects in IB, and pointing the controller at them (control+click, drag, release). This can actually be a bit deceiving. I select my controller and drag the connection over to my PolygonShape class and the polygon iVar within the controller appears in the pop-up. I would have expected the pop-up to contain vars associated with the PolygonShape class.