Threading intro

June 15th, 2009

While my app grabs data from the net I want the user to see a loading animation. To do this I need to break off the main thread and start a new one for the net load, and use the main thread to display the animation. I’ve set up my load animation using a nib, though this is simple enough to where I’d probably prefer to just do it programmatically. But that’s not really what I care about right now. Anyway, my nib is called “LoadSpinner”, and the controller is called “LoadSpinnerController”. Simple first step is to load this view (it subclasses UIView) and add it to the application view. Easy enough, done this quite a few times now:

spinnerController = [[LoadSpinnerController alloc] initWithNibName:@”LoadSpinner” bundle:nil];
[window addSubview:spinnerController.view];

Next I want to start the loading process, so I detach a new thread from the main thread like so:

[NSThread detachNewThreadSelector:@selector(loadNetInfo) toTarget:self withObject:nil];

where

loadNetInfo

is the method used to grab whatever data I need. Outlined here:

-(void) loadNetInfo
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

doing the loading stuff here..

[self performSelectorOnMainThread:@selector(releaseSpinner) withObject:nil waitUntilDone:NO];
[pool release];
}

Notice the selector is looking for

releaseSpinner

. I don’t know if this is the best way to do it, but all I’m doing in this method is

[spinnerController release];

. This gets rid of the spinner when the info is returned, loaded and ready for use. I’ll have to see if there’s a better way to do this.

Presence 2

June 10th, 2009

I found cell rendering to be the biggest challenge in this assignment.

The process of making this work is a bit drawn out. The first thing I need to do is calculate what my

rowHeight

needs to be. This is done by overriding the

heightForRowAtIndexPath

method in my TableViewController.

  1. Grab the string that needs to fit in the cell.
  2. Set its font size.
  3. Create a
    CGSize

    object using my cell width (I used a static width in this case) and a max hieght of 1000 (hopefully I wouldn’t need more height than that). This is done using

    CGSizeMake(280, 1000);
  4. Assign it a size based on the font, font size, and line break mode
  5. Then return the height, with a bit of padding
    return size.height + 20;

Once the row height has been calculated I init my label with a

CGRect

that specifies the label size based on the dimensions of my cell, then I add it to the

contentView

of the cell.
Now in order to make the label multiline enabled I first need to specify the

numberOfLines

property. I’m setting it to equal 0. This means that I’ll get as many lines as needed to fit the text (I don’t know exactly why this works the way it does. I could also put it some arbitrary number, and it would still work, but the maximum number of line breaks would be limited by that number). Then I need to set the

autoResizingMask

property of my label to

UIViewAutoresizingFlexibleHeight

. Also, I have to make sure I’m using the same font and font size that I specified in the

heightForRowAtIndexPath

method (

setFont:[UIFont systemFontOfSize:14.0]

). The it’s just a matter of setting the text value, adding it to the cell and releasing it.

Presence 1

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)