UIAlertView with UITextField, Revisited

My first ever blog post here was on putting a text field on a UIAlertView. While that technique still works perfectly fine, there is now (since iOS 5), an official API for putting a text field on an alert view. All you need to do is set the UIAlertView's alertViewStyle to UIAlertViewStylePlainTextInput. You can then access the text field using textFieldAtIndex:.

- (void) promptForName 
{
    UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Congratulations!" 
                   message:@"You earned a top score! Enter your name:" 
                  delegate:self 
         cancelButtonTitle:nil 
         otherButtonTitles:@"OK", nil]; 
    alert.alertViewStyle = UIAlertViewStylePlainTextInput; alert.delegate = self; [alert show];
}

- (void) alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    UITextField* nameField = [alert textFieldAtIndex:0];
    NSLog( @"You entered %@", nameField.text );
}

Of course, this also works with ZPAlertView.

- (void) promptForName
{
    ZPAlertView* alert = [[ZPAlertView alloc] initWithTitle:@"Congratulations!"
                    message:@"You earned a top score! Enter your name:"
                   delegate:self
          cancelButtonTitle:nil
          otherButtonTitles:@"OK", nil];
    alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    alert.didDismissBlock = ^(NSInteger buttonPressed) {
        UITextField* nameField = [alert textFieldAtIndex:0];
        NSLog( @"Your entered %@", nameField.text );
    };

    [alert show];
}

If you want to follow me, I'm @zpasternack on Twitter and on app.net.

Taking advantage of the iPhone 5’s larger screen

Because it comes up on StackOverflow, I dunno, maybe five times per day, I thought I’d make a quick post describing how to make your apps work well with the new iPhone 5 screen dimensions (while still remaining compatible with older iPhones). It’s really not that hard at all. TaskLog had iPhone 5 support when it launched. It took me about 5 minutes to make this work, and probably a few additional hours to make it work well. The first step is to include an iPhone 5 specific default launch image. This is what iOS uses to determine whether or not your app is ready for the larger screen size. Iphone5 launch image It should be 640 x 1136 pixels. If you drag it into the Launch Images in Xcode, it will be automatically named for you. If you just add it to the project yourself, make sure you name it correctly: “Default-568h@2x.png”. Once you do this, your app will be sized to fit the iPhone 5 screen. From there, the amount of work you need to do depends entirely on how your application is built. If you’ve set up all your views with proper resizing characteristics (either via Auto Layout or the old-school autoresizing masks), you might be done. Just make sure to test it out in the Simulator (choose Device->Hardware->iPhone 5 (Retina) ) and make sure all your views look nice. I can’t stress this enough: test every one of your views on both 3.5″ and 4″ devices (or the simulator, at the very least). Places where you might have gotchas: if you’re doing any kind of hardcoding of coordinates, there might be some tweaking you need to do. If you’re going to move or size things in code, based on coordinates, at least try to do so via relative, rather than absolute, coordinates.

Related StackOverflow questions:

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

High resolution timing in Cocoa

For profiling app performance, it’s necessary to accurately time your code. The best way to do this is to use mach_absolute_time. In conjunction with mach_timebase_info, you can get extremely high resolution timing to ease performance benchmarking. I’ve wrapped this functionality up in a little class to make it super easy to use.

// MachTimer.h
#include <mach/mach_time.h>

@interface MachTimer : NSObject
{
    uint64_t timeZero;
}

+ (id) timer;

- (void) start;
- (uint64_t) elapsed;
- (float) elapsedSeconds;
@end

// MachTimer.m
#import "MachTimer.h"

static mach_timebase_info_data_t timeBase;

@implementation MachTimer

+ (void) initialize
{
    (void) mach_timebase_info( &timeBase );
}

+ (id) timer
{
#if( __has_feature( objc_arc ) )
    return [[[self class] alloc] init];
#else
    return [[[[self class] alloc] init] autorelease];
#endif
}

- (id) init
{
    if( (self = [super init]) ) {
        timeZero = mach_absolute_time();
    }
    return self;
}

- (void) start
{
    timeZero = mach_absolute_time();
}

- (uint64_t) elapsed
{
    return mach_absolute_time() - timeZero;
}

- (float) elapsedSeconds
{
    return ((float)(mach_absolute_time() - timeZero))
        * ((float)timeBase.numer) / ((float)timeBase.denom) / 1000000000.0f;
}

@end

You’d use it like this:

MachTimer* aTimer = [MachTimer timer];
[self performSomeOperation];
NSLog( @"performSomeOperation took %f seconds", [aTimer elapsedSeconds] );

I’ve used this code for iOS and Mac OS apps, and it works great.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Stuff I Can’t Live Without Part I: conditional NSLogging

I spend a great deal of time watching my code. By that I mean either stepping through it in the debugger, or reading logs generated by it to see what it’s done. You might think you know exactly what your software is doing, but the truth is, if you don’t watch it run, you don’t know.

During the course of development, I usually end up adding a fair amount of logging code, and of course I don’t want that in my production builds. So a while back I went a-Googling for some conditional NSLog calls, and found these great macros, courtesy of Nick Dalton’s iPhoneIncubator. I use them (and repeat them below) nearly verbatim. This is the first thing I add to any project I’m working on. I’m naked without my DebugLog (and not in the good way).

// DebugLog is almost a drop-in replacement for NSLog
// DebugLog();
// DebugLog(@“here”);
// DebugLog(@“value: %d”, x);
// Unfortunately this doesn’t work: DebugLog(aStringVariable); you have to do this instead: DebugLog(@“%@“, aStringVariable);
// Shamelessly stolen (and edited) from http://iphoneincubator.com/blog/debugging/the-evolution-of-a-replacement-for-nslog

#ifdef DEBUG
    #define DebugLog(fmt, ...) NSLog((@“%s:%d “ fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
    #define DebugLogFunc() NSLog( @“%s:%d”, __PRETTY_FUNCTION__, __LINE__ );
#else
    #define DebugLog(...)
    #define DebugLogFunc()
#endif

// AlwaysLog always displays output regardless of the DEBUG setting
#define AlwaysLog(fmt, ...) NSLog((@“%s:%d “ fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

Of course this assumes you have DEBUG #defined in your debug builds somewhere (in Xcode 4 do this in Preprocessor Macros in Build Settings).

The only changes I made over Nick’s is renaming them (I prefer clarity over brevity), minor formatting, and adding the …Func macros for the common (for me) case of simply outputting the function name with no further description. This is useful for making sure the functions you expect to get called, really are.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Loading xib files in iPad/iPhone Universal app

I’m making an iPad enhanced version of PuzzleTiles, and have thus been doing a lot of reading on doing Universal apps.

One thing just about every Universal app has to do is to have separate xib files, because the iPhone and iPad have completely different form factors. I’ve stumbled across this idiom continuously in the past few days:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
    mainMenu = [[MainMenu alloc] initWithNibName:
                 @"MainMenu-iPad" bundle:nil];
} else {
    mainMenu = [[MainMenu alloc] initWithNibName:
                 @"MainMenu" bundle:nil];
}

Well, that’s all well and good, but it turns out that initWithNibNamed:bundle: supports device modifiers. So instead of the above, you can use this:

mainMenu = [[MainMenu alloc] initWithNibName:
             @"MainMenu" bundle:nil];

The magic is in the file naming. Name your files “MainMenu.xib” for the iPhone version and “MainMenu~ipad.xib” for the iPad version. iOS will automatically load the device-specific version when running on that device. Much easier, no?

I found no official Apple documentation which explicitly states that the device modifier mechanism works for xib files, but it definitely works, back to iOS 4.0, near as I can tell. Relevant StackOverflow question here. I’m always on the lookout for ways to remove code, so I’ll take it.

Update: Device modifiers are case sensitive, so make sure you’re doing “MyView~ipad.xib”, not “MyView~iPad.xib”, or else the magic won’t work. Relevant Apple documentation here.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

UIAlertView with blocks, revisited

One issue with my UIAlertView with blocks class is that it only supports willDismiss, not any other delegate methods supported by UIAlertViewDelegate. At the time I wrote it, that was all I needed, but more recently, my requirements changed. (Hint: if you’re presenting a UIActionSheet from within a willDismiss delegate method call, bad things will probably happen, as of iOS 4.2).

So I’ve updated it to support all the other UIAlertViewDelegate methods. It’s pretty straightforward.

First we make typedefs for each type of block, one corresponding to each UIAlertViewDelegate method.

typedef void (^WillPresentBlock)(void);
typedef void (^DidPresentBlock)(void);
typedef void (^DidCancelBlock)(void);
typedef void (^ClickedButtonBlock)(NSInteger);
typedef void (^WillDismissBlock)(NSInteger);
typedef void (^DidDismissBlock)(NSInteger);

Then we need an instance variable, and a property for each.

@interface ZPAlertView : UIAlertView
{
    WillPresentBlock willPresentBlock;
    DidPresentBlock didPresentBlock;
    DidCancelBlock didCancelBlock;
    ClickedButtonBlock clickedButtonBlock;
    WillDismissBlock willDismissBlock;
    DidDismissBlock didDismissBlock;
}
@property (nonatomic, copy) WillPresentBlock willPresentBlock;
@property (nonatomic, copy) DidPresentBlock didPresentBlock;
@property (nonatomic, copy) DidCancelBlock didCancelBlock;
@property (nonatomic, copy) ClickedButtonBlock clickedButtonBlock;
@property (nonatomic, copy) WillDismissBlock willDismissBlock;
@property (nonatomic, copy) DidDismissBlock didDismissBlock;
@end

Note that the properties are set for copy. Retaining a block doesn’t work as you’d expect; we need to make a copy.

We synthesize our properties. No need for custom stuff here.

@synthesize willPresentBlock;
@synthesize didPresentBlock;
@synthesize didCancelBlock;
@synthesize clickedButtonBlock;
@synthesize willDismissBlock;
@synthesize didDismissBlock;

Since we’ve made our properties copy, we need to make sure we release them in dealloc.

- (void) dealloc
{
    [willPresentBlock release];
    [didPresentBlock release];
    [didCancelBlock release];
    [clickedButtonBlock release];
    [willDismissBlock release];
    [didDismissBlock release];

    [super dealloc];
}

The real magic is in our overridden show method, which doesn’t look very much like magic at all:

- (void) show
{
    self.delegate = self;
    [super show];
}

Finally we have the actual delegate method calls, each of which call the appropriate block. That’s it.

- (void) willPresentAlertView:(UIAlertView*)alertView
{
    if( willPresentBlock != nil ) {
        willPresentBlock();
    }
}

- (void) didPresentAlertView:(UIAlertView*)alertView
{
    if( didPresentBlock != nil ) {
        didPresentBlock();
    }
}

- (void) alertViewCancel:(UIAlertView *)alertView
{
    if( didCancelBlock != nil ) {
        didCancelBlock();
    }
}

- (void) alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if( clickedButtonBlock != nil ) {
        clickedButtonBlock(buttonIndex);
    }
}

- (void) alertView:(UIAlertView*)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if( willDismissBlock != nil ) {
        willDismissBlock(buttonIndex);
    }
}

- (void) alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if( didDismissBlock != nil ) {
        didDismissBlock(buttonIndex);
    }
}

The client code would look like this:

- (void) doLengthyOperationPrompt
{
    enum {
        ButtonIndexCancel = 0,
        ButtonIndexDoItNow,
        ButtonIndexDoItLater
    };

    ZPAlertView* anAlert = [[ZPAlertView alloc] initWithTitle:@"Warning!"
        message:@"Would you like to perform a really lengthy operation?"
        delegate:nil
        cancelButtonTitle:@"Nope"
        otherButtonTitles:@"Yeah, sure", @"Meh, maybe later", nil];
    anAlert.didDismissBlock = ^(NSInteger buttonIndex){
        switch( buttonIndex ) {
            case ButtonIndexDoItNow:
                [self performLengthyOperation];
            break;
            case ButtonIndexDoItLater:
                [self scheduleLengthyOperationForLater];
            break;
        }
    };
    [anAlert show];
    [anAlert release];
}

I’ll try to put the full source and and example project up somewhere, soon. Any and all feedback is welcome.

Update: It’s now up on github, here.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Why Didn’t It Come With This? Part 1

Sometimes I find myself writing the same function over and over, and I wonder, “Why didn’t it come with this?” Here’s one I did today. I have a UIImage which I want to scale to a different size. Actually, this happens in 3 or 4 places in PuzzleTiles. I finally broke down and made it a category on UIImage. Hopefully you find it useful as well.

// UIImage+Resizing.h
@interface UIImage (Resizing)

- (UIImage*) scaledImageWithSize:(CGSize)newSize;

@end


// UIImage+Resizing.m
#import "UIImage+Resizing.h"

@implementation UIImage (Resizing)

- (UIImage*) scaledImageWithSize:(CGSize)newSize
{
    UIGraphicsBeginImageContextWithOptions( newSize, NO, 0.0 );
    CGRect scaledImageRect = CGRectMake( 0.0, 0.0, newSize.width, newSize.height );
    [self drawInRect:scaledImageRect];
    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return scaledImage;
}

@end

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

UIAlertView with blocks

When extending behavior in Objective-C (unlike in other OO languages), subclassing is not always your first choice. In particular, the Cocoa framework makes extensive use of the Delegation pattern. While this works very well in general, it can at times become cumbersome. Consider a ViewController that wants to display an alert:

//  MyViewController.m
#import “MyViewController.h”

enum {
    ButtonIndexCancel = 0,
    ButtonIndexDoItNow,
    ButtonIndexDoItLater,
    LengthyOperationAlertTag = 100
};

@implementation MyViewController

- (void) doLengthyOperationPrompt
{
    UIAlertView* anAlert = [[UIAlertView alloc] initWithTitle:@"Warning!"
                    message:@"Would you like to perform a lengthy operation?"
                   delegate:self
          cancelButtonTitle:@"Nope"
          otherButtonTitles:@"Yeah, sure", @"Meh, maybe later", nil];
    anAlert.tag = LengthyOperationAlertTag;
    [anAlert show];
    [anAlert release];
}

- (void) alertView:(UIAlertView*)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if( alertView.tag == LengthyOperationTag ) {
        switch( buttonIndex ) {
            case ButtonIndexDoItNow:
                [self performLengthyOperation];
            break;
            case ButtonIndexDoItLater:
                [self scheduleLengthyOperationForLater];
            break;
        }
    }
}
@end

But Zach, I hear you say, what’s wrong with that? And I agree, it’s not too terrible taken on it’s own. Thing is, I’m betting MyViewController does a whole lot of things other than showing an alert. Meaning, the chances are good that your doLengthyOperationPrompt and alertView:willDismissWithButtonIndex: methods are not actually right next to each other. And, they’re probably not at the top either. So you have three related pieces of code (the enums, the code to show the alert, and the code to handle the button press) which are most likely physically segregated from one another. Now imagine MyViewController has a half-dozen different UIAlertViews, each with different buttons and tags and code called in response. It adds up to a whole lotta ugly pretty quickly.

Wouldn’t it be better if all three related pieces of code were in the same place? In my dream, it would look like this:

//  MyViewController.m
#import “MyViewController.h”

@implementation MyViewController

- (void) doLengthyOperationPrompt
{
    enum {
        ButtonIndexCancel = 0,
        ButtonIndexDoItNow,
        ButtonIndexDoItLater
    };

    UIAlertView* anAlert = [[UIAlertView alloc] initWithTitle:@"Warning!"
                    message:@"Would you like to perform a lengthy operation?"
                   delegate:self
          cancelButtonTitle:@"Nope"
          otherButtonTitles:@"Yeah, sure", @"Meh, maybe later", nil];
    [anAlert showWithCompletion:^(NSInteger buttonIndex) {
        switch( buttonIndex ) {
            case ButtonIndexDoItNow:
                [self performLengthyOperation];
            break;
            case ButtonIndexDoItLater:
                [self scheduleLengthyOperationForLater];
            break;
        }
    }];
    [anAlert release];
}
@end

The code that handles the button presses is right there next to the code that displays the alert, preventing you from having to scroll around to follow the flow. In addition, because they’re in the same scope, we don’t have to declare the enum at the top – this is the only function that uses it – so it’s also there in the same place. Nice and compact, and all the related functionality is in close physical proximity. But how do we get there?

As I mentioned before, subclassing isn’t always the first choice as a means to extend functionality. My knee-jerk inclination was to implement this as a class extension to UIAlertView. Unfortunately, extensions can only add member functions, not iVars, making this approach problematic. The easy way, in this case, is to subclass.

It seems simple enough, we’ll need a showWithCompletion: member function into which we pass our block. Our class will need an iVar to hold the block. Then showWithCompletion: can set delegate to itself, and call the block on it’s own alertView:willDismissWithButtonIndex:.

It looks a little somethin’ like this:

//  ZPAlertView.h
#import <Foundation/Foundation.h>

typedef void (^AlertCompletion)(NSInteger);

@interface ZPAlertView : UIAlertView
{
    AlertCompletion completionBlock;
}
- (void) showWithCompletion:(AlertCompletion)aBlock;
@end


//  ZPAlertView.m
#import "ZPAlertView.h"

@implementation ZPAlertView

- (void) showWithCompletion:(AlertCompletion)aBlock
{
    self.delegate = self;
    completionBlock = [aBlock copy];
    [self show];
}

- (void) alertView:(UIAlertView*)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    completionBlock(buttonIndex);
    [completionBlock release]; completionBlock = nil;
}

@end

Stupid simple, right?

We start out by creating a typedef for our block. It takes a single parameter of type NSInteger and has no return value. This isn’t quite the same as alertView:willDismissWithButtonIndex, because we don’t supply the alertView. There’s no need to, because it’s right there in the scope of our block. We add one function, showWithCompletion:, which takes the block which will be executed.

showWithCompletion: sets the delegate to self (because we’ll be handling alertView:willDismissWithButtonIndex: ourselves), stores off a copy of the completion block, and calls [self show]. We take a copy of the block, because the block was created on the stack, and will be going out of scope before we execute it later, so the copy gives a copy on the heap which will stick around until we’re done with it.

The magic happens in alertView:willDismissWithButtonIndex:. We simply call the block, and then release it. That’s it.

Using this class, we can do exactly like I wanted above, by only replacing UIAlertView with ZPAlertView:

//  MyViewController.m
#import “ZPAlertView.h”
#import “MyViewController.h”

@implementation MyViewController

- (void) doLengthyOperationPrompt
{
    enum {
        ButtonIndexCancel = 0,
        ButtonIndexDoItNow,
        ButtonIndexDoItLater
    };

    ZPAlertView *anAlert = [[ZPAlertView alloc] initWithTitle:@"Warning!"
                    message:@"Would you like to perform a lengthy operation?"
                   delegate:self
          cancelButtonTitle:@"Nope"
          otherButtonTitles:@"Yeah, sure", @"Meh, maybe later", nil];
    [anAlert showWithCompletion:^(NSInteger buttonIndex) {
        switch( buttonIndex ) {
            case ButtonIndexDoItNow:
                [self performLengthyOperation];
            break;
            case ButtonIndexDoItLater:
                [self scheduleLengthyOperationForLater];
            break;
        }
    }];
    [anAlert release];
}
@end

You can do lots of cool stuff with this. Want to put a text field on there? No problem!

- (void) doAlertWithTextField
{
    ZPAlertView *alert = [[ZPAlertView alloc] initWithTitle:@"Hello!"
                    message:@"Please enter your name:\n\n\n"
                   delegate:nil
          cancelButtonTitle:nil
          otherButtonTitles:@"OK", nil];
    UITextField *nameEntryField = [[UITextField alloc] initWithFrame:CGRectMake(12, 90, 260, 25)];
    nameEntryField.backgroundColor = [UIColor whiteColor];
    nameEntryField.keyboardType = UIKeyboardTypeAlphabet;
    nameEntryField.keyboardAppearance = UIKeyboardAppearanceAlert;
    nameEntryField.autocorrectionType = UITextAutocorrectionTypeNo;
    nameEntryField.clearButtonMode = UITextFieldViewModeWhileEditing;
    [alert addSubview:nameEntryField];
    [nameEntryField becomeFirstResponder];
    [nameEntryField release];
    [alert showWithCompletion:^(NSInteger buttonIndex) {
        UIAlertView *anAlert = [[UIAlertView alloc] initWithTitle:@"Greetings!"
                        message:[NSString stringWithFormat:@"Hello, %@", nameEntryField.text]
                       delegate:nil
              cancelButtonTitle:nil
              otherButtonTitles:@"OK", nil];
        [anAlert show];
        [anAlert release];
    }];
    [alert release];
}

Normally when you do such a thing (as I’ve written about before), you need to set a tag on the UITextField so you can find it later in alertView:willDismissWithButtonIndex to get at the text. This way, there’s no need for that, because it’s still in the scope of our block.

One limitation of this is that UIAlertViewDelegate has a bunch of delegate methods aside from alertView:willDismissWithButtonIndex:. If you wanted to do something in other delegate methods, you’d need to modify it to do that. For my purposes, all I needed was willDismissWithButtonIndex. Also, showWithCompletion: is replacing whatever delegate you specified in your initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles: call, so you can’t really use any of the other delegate methods (of course, you could just call show if you wanted to do that, or just use a regular UIAlertView).

One could probably implement this as a class extension, but there are issues with doing so. For one thing, you can’t add any iVars to a class extension, only member functions. You could get around this by, say, storing a static dictionary mapping UIAlertViews to AlertCompletion blocks. But that is kinda icky. Plus, the semantics of an NSDictionary are to copy the key, so you couldn’t use the UIAlertView as the key, unless you take the integer value of the pointer and wrap that in a NSNumber. Double icky. That’s about as far along as I’ve gotten in the process of making this into a class extension, but if someone has a better idea, I’d love to hear it.

Note that I haven’t used this in any production code yet – PuzzleTiles 1.1 is close enough to release that I’m disinclined to make such seemingly gratuitous changes – so I don’t know if there are any gotchas I haven’t thought of. Use it at your own risk, I’m saying. If you do find use for it, or have ideas for how to improve it, let me know!

Update: The above paragraph is no longer true; I’m using this code (actually, an updated version of this code) in both PuzzleTiles and PuzzleTiles HD.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Stupid UIAlertView Tricks Part Deux

Another Stackoverflow question has prompted me to yet again revisit the issue of putting random views into a UIAlertView.

If you missed my previous installments, check out UIAlertView with UITextField (which is actually moderately useful at times), and UIAlertView with UIWebView, which is pretty much not good for anything except guffaws.

In this installment, we’ll jam a UITableView in there, and even throw in some UISwitch controls for good measure.

Note: never do this. Though I don’t believe it’s a direct violation of the Almighty Apple Book Of Holiness Human Interface Guidelines, it would surely make most iPhone UI Geeks cringe. I’m not a UI hardliner, but I wouldn’t do this in one of my apps. Neither Fat Apps, nor myself are responsible for any App Store rejections, loss of life, loss of limb, or loss of credibility that may result from using this code in an actual app. This is for fun.

Now then. Much like putting any other random view in an alert view, there’s really not that much work to do beyond what you’d normally do if you were making such a view without the alert view. In the case of a UITableView, you need to have a class which implements UITableViewDataSource. Create the cells in tableView:cellForRowAtIndexPath:, and add your controls as the accessoryView, like so:

- (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    static NSString* const SwitchCellID = @"SwitchCell";
    UITableViewCell* aCell = [tableView dequeueReusableCellWithIdentifier:SwitchCellID];
    if( aCell == nil ) {
        aCell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:SwitchCellID] autorelease];
        aCell.textLabel.text = [NSString stringWithFormat:@"Option %d", [indexPath row] + 1];
        aCell.selectionStyle = UITableViewCellSelectionStyleNone;
        UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
        aCell.accessoryView = switchView;
        [switchView setOn:YES animated:NO];
        [switchView addTarget:self action:@selector(soundSwitched:) forControlEvents:UIControlEventValueChanged];
        [switchView release];
    }

    return aCell;
}

There’s no magic here: it’s what I do in the PuzzleTiles options screen, and it’s what Apple does in the Settings app. No big whoop.

Alright, now let’s jam this bad boy into an alert view.

- (void) doAlertWithTableView
{
    UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Preferences" 
                        message:@"\n\n\n\n\n\n\n"
                       delegate:self 
              cancelButtonTitle:@"Cancel"
              otherButtonTitles:@"OK", nil];

    UITableView* myView = [[[UITableView alloc] initWithFrame:CGRectMake(10, 40, 264, 150) 
              style:UITableViewStyleGrouped] autorelease];
    myView.delegate = self;
    myView.dataSource = self;
    myView.backgroundColor = [UIColor clearColor];
    [alert addSubview:myView];

    [alert show];
    [alert release]; 
}

That’s it! The only real trick (just like with a UITextField or a UIWebView, is to put some linefeeds in your message to make the alert taller to fit your view.

Here’s how she looks:

Uialert with table

Next time I’ll try writing a post that doesn’t feature a UIAlertView! Yay!

If you want to follow me, I’m @zpasternack on Twitter and on app.net.