The quest for a better UIAlertView

One of the things that has annoyed me while developing my iPhone game “Wordly” are dialogs that appear modal to the user, but are non-blocking from the programmer perspective. For example, let’s take UIAlertView.

You tell a UIAlertView to show itself and the user sees a modal dialog, but the code after your show message keeps executing, before the user even clicks anything. The way to find out which button the user clicked is via a callback; the UIAlertView calls the alertView:clickedButtonAtIndex: method of the delegate object (an object that conforms to the UIAlertViewDelegate protocol) that you supply.

So using the calling object as the delegate, it looks something like this:

A few things bother me about this style:

  1. Displaying a modal dialog and then reacting to the choice is conceptually a simple, sequential process, but you end up splitting your steps between two methods. The first method doesn’t even mention the name of the second method, so for programmers who are unfamiliar with Cocoa or just forgetful of what the magic method is in UIAlertViewDelegate, it’s not immediately clear how the code flows.
  2. To decide how to handle a button press, you have to match against a button index or a button title that you used in the other method. This is in my mind a violation of the DRY principle and it’s error prone.
  3. If your class is the delegate for a bunch of dialogs, stuff starts to get messy pretty quickly, as your method needs to distinguish between different dialogs by looking at the alert view’s tag or otherwise inspecting it.

Basically, it’s not as readable as I would like. It seems I am not alone:

Interestingly, the authors of the latter two blog posts also develop solutions that are similar to my first solution below. I’ve not yet found anything that looks like the second solution that I discuss below.

My first idea for how to make things more intuitive was to create a new class called ModalAlertView. Basically it just polls in a while loop, until the callback gets called. NSRunLoop‘s runUntilDate method is used in the while loop to let the event loop process events – here’s the relevant part of the code:

Now you can use this modal dialog something like this:

This seems to work pretty well, but it still bothers me. Polling is wasteful and we’re talking about mobile devices here, so we’re wasting processor time and battery life and when we have multitasking in iPhone OS 4, this will be wasting time that other processes could be using. The API also seems to really be pushing the developer in the direction of having non-blocking code and this approach kind of subverts that. I’d rather not go against the grain.

So I searched for another solution. For a while I thought the problem I was trying to solve was how to create a modal dialog. But then it dawned on me that the real problem was that I wanted a more intuitive way to create and react to dialogs. Making the dialog modal was one way to make things more intuitive to my sequential brain, but there was perhaps another way.

The other way was to create a class that displays the dialog and then acts as the delegate for that dialog, doing dispatching to whatever methods the caller specifies for the buttons, thereby relieving the caller of the responsibility of doing the dispatching. The caller uses the new API like this:

Concise. Simple. And it can be implemented with pretty simple Objective-C, without funky polling and run loops. Here’s what the implementation looks like:

Nothing too hairy in there, that I can see.

It gives you a simple API that lets you create buttons and link them to specific handler methods. You don’t even need to worry about releasing the dialog object as it adds itself to the autorelease pool after the callback fires.

Objective-C afficionados, please send me any suggested improvements…

11 thoughts on “The quest for a better UIAlertView

  1. Thank you very much for that article. Actually, I really love both approaches. I agree with you to better not take the first one. But there are circumstances, where you basically have no choice (or I don’t see it).

    I want to show a message box right on startup when my app has been called via an URL handler. But if I show the message box in

    – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    But do not block until the user clicks, the method returns and there is no way to build the UI afterwards (e.g. in the alertView delegate).

  2. Hi Marc,

    Thanks for posting all this. I am new to objc and not qute sure how to implement the above. I’m just looking for a modal to warn users when they change settings to invalid or questionable values.

    When I paste your code it complains about undeclared vars and first use for DispatchingAlertView and alertView.

    Please advise and thanks again!

    Ron

  3. Thanks a lot for the article – as a newbie moving from .NET it seems like a strange world. Ok so I chickened out of the second approach on this occasion but hope to come back to it sometime.

  4. One thing you have to be careful of is thread safety, for instance calling [alertView show] on any other thread besides the main thread is bound not to do what you want it to do(most of the time it will just make the screen go dark and prevent the user from clicking any buttons). Plus Apple goes out of its way to essentially say that run loops aren’t thread safe, so there is probably something there….

    I was always a fan of the NSRunAlertPanel available to the macs and I came across this page while trying to find a substitute. I ultimately just decided to roll my own. My philosophy behind it was, if they executed the request on the main thread, do the callbacks on the main thread. Otherwise do the callbacks on another thread(though I may want to tweak that to give an option to force it to do the callbacks on the main thread)

    I checked everything into google code if you want to see what it does for yourself:

    https://code.google.com/p/jtrunalertpanel/

    It’s set up to build as a library so you can integrate it into other projects.

  5. Hi.
    Thanks for the approach.
    Maybe I’m made but I could not see the “result” you talked about when you said : “Here’s what the implementation looks like:”
    As well as no code…
    Is there a problem on this article ? Or do I have misunderstood ?

  6. Thank you very much for that article. Actually, I really love both approaches. I agree with you to better not take the first one. But there are circumstances, where you basically have no choice (or I don’t see it). I want to show a message box right on startup when my app has been called via an URL handler. But if I show the message box in – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { But do not block until the user clicks, the method returns and there is no way to build the UI afterwards (e.g. in the alertView delegate).

  7. I decided to tackle with that in a different way.
    First I have a singleton for the alertview, which offers a simplified way to implement alerts, see the interface bellow.
    Than, I created a way to register a object/selector/parameter to be called after the click (also bellow).
    Thus, if you want to make it modal, you split your function:

    #### usage:
    – (void) using {
    [AlertUtils alertOkWithTitle:@”An error happened”];
    [AlertUtils setSelector:@selector(continueAfter) of:self with:nil];
    NSLog(@”this line happens right away”);
    }
    – (void) continueAfter {
    NSLog(@”this line only after close dialog”);
    }

    #### Interface:

    +(void) alertOkWithTitle:(NSString*)title type:(int)type;
    +(void) alertWithMessageTitleAndIndicator:(NSString*)title message:(NSString*)message;
    +(void) alertWithTitleAndIndicator:(NSString*)title;
    +(void) alertOkNoWithTitle:(NSString*)title withDelegate:(id)delegate;
    +(void) stop;
    +(void) stopAfter:(float)delay;

    +(void) setSelector:(SEL)selector of:(id)object with:(id)param;

  8. Your DispatchingAlertVIew class is very nicely done, especially the use of valueWithPointer, rather than using NSSelectorFromString() or some other funky workaround to pass selectors.
    It does seem cleaner than implementing alertView:clickedButtonWithIndex: with tag values.

    However, using the blocking example may also prove useful, such as when you’re in a “should” delegate call, like textFieldShouldBeginEditing and you want to solicit user input to control either commencing editing or cancelling out of it. If I automatically cancel out of it and wait until the dispatchingalertview returns a response, I then need to programmatically re-initiate editing of the uitextfield, which seems kludgey…

    May I use this your code in my projects?

  9. Yes you may use this code in your projects. An acknowledgement in the README would be appreciated, but is not required.

  10. Hi, i want to know how you implement a non-blocking uialertview without runloop. I have used uialertview with runloop but it did not act right in some my view. But i can not see your code. Could you send me ? My email: whf881211@qq.com Thx a lot.

Leave a Reply

Your email address will not be published.