Getting Objective, part 3: How is the world, anyway?


7 January 2012, by

previous article in series

Recap

In my first post, I wrote a very simple “hello world” application, that displayed an alert box like this:

Screenshot of the Hello World application popup

It’s lovely as far as it goes, but you were to install that application on your phone, you would no doubt become rapidly disillusioned with it – it may ask how you are, but ideally we’d want something to happen once we press a button. At the moment, all that would happen is that the popup would disappear, and you’d be looking at a white space. Admittedly, this is pretty much how some of the early torch applications worked, but I think we can all agree that we’re not (yet) putting Objective-C to its best use.

Learning to delegate

Ideally, we would like our application to take action when you press one of the buttons on the popup – and, indeed, to take different action depending which button you pressed. The code that made our popup looked like this:

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hello World!"
                                              message:@"How are you today?"
                                              delegate:nil
                                              cancelButtonTitle:@"Fine thanks"
                                              otherButtonTitles:@"A little tired", nil];

Now that we’re familiar with the square brackets, we can happily interpret that command. First up, what is the recipient of our message? There’s some nested brackets, but the first Object in our code snippet is:

[UIAlertView alloc]

This simply allocates a block of memory to be used as a UIAlertView, which is the standard alert popup implementation in the Apple SDK.

So now we have a recipient, what’s the rest of our message? Everything else:

initWithTitle:@"Hello World!"
message:@"How are you today?"
delegate:nil
cancelButtonTitle:@"Fine thanks"
otherButtonTitles:@"A little tired", nil

I imagine that you can interpret most of that without much assistance. We are initializing our alert with a few parameters, the majority of which correspond to the text in our screenshot.

A slight detour…

Before I dive in to the bit that matters, why is there a nil right at the very end? The clue to that is in the name of the parameter – it says otherButtonTitles, not otherButtonTitle. Our popup can have as many “other” buttons as we like, but how would we put in more?

otherButtonTitles:@"A little tired", @"Sad", nil

This would give us a “Sad” button (not that anyone at Softwire would click it), but there’s still the nil at the end – in fact, the nil defines the end. When our code is executed and something is trying to actually render our popup, it needs to know when it’s reached the end of the list of other buttons, and the nil does exactly that.

Back on track

So we want something to respond to the buttons on our alert. Our message currently only sends a load of strings to a UIAlertView. How do we monitor what happens to the alert and take appropriate action? We have one parameter that is as yet unused and unexplained:

delegate:nil

The delegate for our alert view is the object that will receive a message when the alert is dismissed. If we set the delegate to nil, as we have at the moment, no message gets sent. In a more complicated application, we would probably be sending the results of our alert elsewhere, but in the interests of simplicity we’ll keep everything in a single file by setting the delegate to “self”. In order to do this, we’ll need to tell the compiler that our file is compatible with the messages that are going to be sent. This is done by implementing the UIAlertViewDelegate protocol1 – in this particular case, you just have to open up HelloWorldAppDelegate.h and change the declaration from:

@interface HelloWorldAppDelegate <UIApplicationDelegate> : NSObject

to:

@interface HelloWorldAppDelegate <UIApplicationDelegate, UIAlertViewDelegate> : NSObject

Getting the message

The next step is to make sure that we can understand the messages being sent. There are several different callbacks that get triggered when you dismiss an alert, but fortunately (given our experiences with Chen last time), the alert checks that its delegate understands them all before sending. We’ll just implement one of them for now:

- (void)alertView:(UIAlertView *)alertView
        didDismissWithButtonIndex:(NSInteger)buttonIndex;

If you’re not sure what that function definition means, then you definitely haven’t been paying attention so far!

The thing we’ll be interested in here is the buttonIndex, which for our particular alert will be 0 or 1, as we have two buttons (we don’t need the sad button!). All we need to do, therefore, is to check which button was pressed and take appropriate action. Let’s see the whole file:

//
//  helloworldAppDelegate.m
//  helloworld
//

#import "HelloworldAppDelegate.h"

@implementation HelloworldAppDelegate

@synthesize window;

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hello World!"
                                                    message:@"How are you today?"
                                                   delegate:self
                                          cancelButtonTitle:@"Fine thanks"
                                          otherButtonTitles:@"A little tired", nil];
    [alert autorelease];

    [window makeKeyAndVisible];
    [alert show];
    return YES;
}

- (void)alertView:(UIAlertView *)alertView
        didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 0) {
        // They're fine, let's Rick-roll them!
        NSURL *rickRoll = [NSURL URLWithString:@"http://www.youtube.com/v/oHg5SJYRHA0"];
        [[UIApplication sharedApplication] openURL:rickRoll];
    } else if (buttonIndex == 1) {
        // They're tired - how about a cup of coffee?
        UIImage *image = [UIImage imageNamed:@"coffee.jpg"];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        [imageView setFrame:CGRectMake(14, 50, 293, 284)];
        [window addSubview:imageView];
    }
}

- (void)dealloc
{
    [window release];
    [super dealloc];
}

@end

Our application now Rick-rolls anyone who says they’re fine, redirecting into the YouTube app, and shows a picture of some coffee to anyone who says they’re a little tired:

Screenshot of our coffee view

Though this is contingent on there actually being a file in the application called “coffee.jpg” for it to find!

Next Time

In my final post, I will tell you why the code above is very bad indeed! (And hopefully teach you how to avoid writing similar bad code yourself…)

Footnote

1A protocol is simply a header file that defines functions that are implemented in the main file.

next article in series

Tags: , , , , , , ,

Categories: Mobile, Technical

«
»

Leave a Reply

* Mandatory fields


9 + two =

Submit Comment