Archive for the ‘Objective-C’ Category

Presence 3 – Part 2 Custom Delegation

Thursday, August 6th, 2009

Setting up a custom delegate seems pretty straightforward. In this case we want the calling class to understand what the child is saying to it.

I’ve set up my PersonListTableViewController with a toolbar item that allows me to update my twitter status. It does so by popping up a new modal window where I type in my status and hit done to send it over to twitter. Here’s where I do that. In the main view controller I add the method that will trigger my modal window. didTapEditStatusButton:

-(void)didTapEditStatusButton
{
StatusComposeViewController *statusComposeViewController = [[StatusComposeViewController alloc] init];
statusComposeViewController.delegate = self;

[self presentModalViewController:statusComposeViewController animated:YES];
[statusComposeViewController release];
}

The first thing that happens here is the modal window is instantiated.
Next I assign the delegate of the modal window to the main view controller. This is how we get the parent to listen to the child. Or, I guess this is where we tell the child who it’s daddy is.
When this is done all we need to do is present the controller, then release.

Because it is considered “Bad Form” for a modal window to dismiss itself, I’ll use delegation to let my parent view controller PersonListTableViewController know what the child view controller (my modal window) has done.

So the first thing I need to do is create my modal view. I’m going with the suggested StatusComposeViewController. Nothing new here, I’m subclassing UIViewController.

In the class header for my new view I need to add the delegation protocol. First thing I need to remember to do is declare the delegate protocol.

@protocol StatusComposeViewControllerDelegate;

Then I create the protocol and give it a couple of optional methods:

@protocol StatusComposeViewControllerDelegate
@optional
-(void)statusViewControllerDidSetStatus:(StatusComposeViewController *)controller didSetStatus:(NSString *)text;
-(void)statusViewControllerDidCancel:(StatusComposeViewController *)controller;
@end

This essentially declares these methods for whatever controller you specify as your delegate.

Moving back into my delegate (PersonListTableViewController) I’m going to need to implement the methods I’ve defined in my modal view. For this post I’ll just do one, the statusViewControllerDidSetStatus method.

-(void)statusViewControllerDidSetStatus:(StatusComposeViewController *)controller didSetStatus:(NSString *)text
{
[TwitterHelper updateStatus:text forUsername:username withPassword:password];
[self didFinishWithModalViewController];
}

A simple method for updating twitter status. The TwitterHelper class is provided in the course files. didFinishWithModalViewController is a method I’m using for cleanup and dismissal of the modal window. (Dismiss using [self didFinishWithModalViewController]). Any view can only have one modal view up at any given time, so we don’t need to tell this method which modal view to dismiss… it will always be the current modal view.

What did I miss…

Ok in the modal view controller StatusComposeViewController I need to implement the methods I’ll use to hit the delegate methods to perform the actions that the modal view is intended for.

- (IBAction)doneButtonPressed:(id)sender{
if([self.delegate respondsToSelector:@selector(statusViewControllerDidSetStatus:didSetStatus:)]){
[self.delegate statusViewControllerDidSetStatus:self didSetStatus:textField.text];
}
}

It is important to make sure you find out weather or not the delegate has implemented the method you’re going to try to call. This is done in the first line. Then we perform the operation.

Presence 3 (Part 1, Threading)

Monday, June 22nd, 2009

Mastering the art of threading using NSOperation is the first part of this assignment. I unfortunately got a bit stuck in the lecture examples. I was trying to implement the example code that I had seen without a solid understanding of what it was doing. This led to a few nights of frustration. I finally decided to scrap my currant process, go back to the code and try to really understand what it was doing. (I know, this is something one should always do, but in my zest to have a working prototype I lost sight of this).

My biggest problem here was that I needed to understand the order of operations a little better. The first thing I need my app to do is get the list of usernames. So, in my table view controllers initWithStyle method I read the names from the TwitterUsers.plist and initialize my names NSArray with the results.

//get the names
usersBundle = [[NSBundle mainBundle] pathForResource:@”TwitterUsers” ofType:@”plist”];
names = [[NSArray alloc] initWithContentsOfFile:usersBundle];

I’ll also use this method to init my NSOperationQueue. I’ll be needing this very soon.

In my viewWillAppear method I’ll call the method to get the list of details for each person. This is going to include the URLs for their respective images. Except, I don’t really call the method that does this. I call the method that adds the method that does this to an NSOperationQueue (getListOfPeople).

-(void)getListOfPeople
{
NSLog(@”(void)getListOfPeople”);
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(syncLoadingPeopleList) object:nil];
[operationQueue addOperation:operation];
[operation release];
}

So, syncLoadingPeopleList is the method that loads the people data into the people array. When this is finished I call the didFinishLoadingPeopleList back in the main thread.

[self performSelectorOnMainThread:@selector(didFinishLoadingPeopleList) withObject:nil waitUntilDone:NO];

The didFinishLoadingPeopleList iterates through the people and adds the image URL to my imageURLList NSMutableArray. Upon writing this, it occurs to me that I could probably just as well populate my imageURLList array in the syncLoadingPeopleList method at the same time I am populating the people array…

Here’s something that gave me some trouble initially. Chalk it up to a dumb mistake. At this point I still had my numberOfRowsInSection method looking at the size of my names array (perfectly ok in Presence 2). The problem here was that I was looking for information in my ImageURLList, which was populated by my people array, which at that point may not have had a single person loaded yet. I was getting all sorts of invalid index errors. Once I switched that out with the people array I was good to go.

So now I’ve got my image URLs, but still no images. Images need to be downloaded from the net and loaded into the table cell. Obviously this calls for some more threading. In my cellForRowAtIndexPath method where I would normally set my image to equal an already existing image, I’m setting it to equal the result of a cached image array lookup. Here’s how this works:

  1. cellForRowAtIndexPath is called nd wants an image
  2. image is set to the cached image
    cell.image = [self cachedImageForURL:[imageURLList objectAtIndex:indexPath.row]];
    • the cachedImageForURL method takes a NSURL as a param and looks to see if it’s NSMutableArray counterpart (cachedImages) already has this image
      id cachedObject = [cachedImages objectForKey:url];
      if(cachedObject == nil){
    • If so, then it checks to make sure it is an NSURL, and sets it to nil if not (remember it’s ok to send a nil message)
      else if(![cachedObject isKindOfClass:[UIImage class]]){
      cachedObject = nil;
      }
    • If not, then it starts the process of downloading the image
      ImageLoadingOperation *operation = [[ImageLoadingOperation alloc] initWithImageURL:url target:self action:@selector(didFinishLoadingImage:)];
      [operationQueue addOperation:operation];
      [operation release];
  3. This executes as many times as there are people in the list, and calls didFinishLoadingImage as soon as the image is loaded and ready for use. This adds the image to the cashedImages array and reloads the table data.
    -(void)didFinishLoadingImage:(NSDictionary *)result
    {
    NSURL *url = [result objectForKey:@"url"];
    UIImage *image = [result objectForKey:@"image"];

    [cachedImages setObject:image forKey:url];
    [self.tableView reloadData];
    }

…and we have our image

Threading intro

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