Posted by: Jake Dempsey on: March 24, 2009
I have been working on a story: “As a user, I should be able to search for hotels around a given location so that I can add the hotel to my trip.”.
I built my basic form for the user to fill out the location information and provided my awesome continue button to get hotels:

I quickly realized that the user’s location could be resolved to more than a single place. If the user entered in county: US and then city: Greenville, there would be 10 or so matches. I would need the user to choose which was the correct city. One way to do this would have been to take the user to a new UITableViewController with all the matches and ask the user to choose a match, then pop then continue on to find the hotels around the selected location. While this would work, I dont like it. What if it worked like Google Maps? The Google Maps application shows the user a UIAlertView with an embedded TableView. It’s pretty clean. It works well because the user does not need to go to a separate page to choose a location match. I want my user to have this same experience.
If you have used the UIAlertView much, you know it is very limited on custimization opportunity. I decided to subclass UIAlertView to build my new location selector. I ran into a few “a ha” moments when trying to mimic the Google Apps look and feel. First, their UIAlertView has rouned top and bottom corners that are independent of the TableView. They are actually using a PlainStyle TableView and when you scroll the table in the alert, the table looks like it is sitting inside of a rounded rect UIScrollView. Tricky Indeed!
Our solution was to use some transparent rounded corners with a dropshadow on the top image. We also wanted the height of the custom alert to be dynamic based on the size of the embedded UITableView. However, UIAlertViews do not have a frame or height property. The height of the UIAlertView is based on the length of its message property. Our solution to this problem was to identify the number of rows in the table and add return characters to the message property of the UIAlertView.
Here is what it ended up looking like:

In case you wanna see some code, Here ya go:
@protocol AlertTableViewDelegate
-(void)didSelectRowAtIndex:(NSInteger)row withContext:(id)context;
@end
@interface AlertTableView : UIAlertView <UITableViewDelegate, UITableViewDataSource>{
UITableView *myTableView;
id<AlertTableViewDelegate> caller;
id context;
NSArray *data;
int tableHeight;
}
-(id)initWithCaller:(id<AlertTableViewDelegate>)_caller data:(NSArray*)_data title:(NSString*)_title andContext:(id)_context;
@property(nonatomic, retain) id<AlertTableViewDelegate> caller;
@property(nonatomic, retain) id context;
@property(nonatomic, retain) NSArray *data;
@end
@interface AlertTableView(HIDDEN)
-(void)prepare;
@end
#import "AlertTableView.h"
@implementation AlertTableView
@synthesize caller, context, data;
-(id)initWithCaller:(id)_caller data:(NSArray*)_data title:(NSString*)_title andContext:(id)_context{
NSMutableString *messageString = [NSMutableString stringWithString:@"\n"];
tableHeight = 0;
if([_data count] < 6){
for(int i = 0; i < [_data count]; i++){
[messageString appendString:@"\n\n"];
tableHeight += 53;
}
}else{
messageString = @"\n\n\n\n\n\n\n\n\n\n";
tableHeight = 207;
}
if(self = [super initWithTitle:_title message:messageString delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil]){
self.caller = _caller;
self.context = _context;
self.data = _data;
[self prepare];
}
return self;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
[self.caller didSelectRowAtIndex:-1 withContext:self.context];
}
-(void)show{
self.hidden = YES;
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myTimer:) userInfo:nil repeats:NO];
[super show];
}
-(void)myTimer:(NSTimer*)_timer{
self.hidden = NO;
[myTableView flashScrollIndicators];
}
-(void)prepare{
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(11, 50, 261, tableHeight) style:UITableViewStylePlain];
if([data count] < 5){
myTableView.scrollEnabled = NO;
}
myTableView.delegate = self;
myTableView.dataSource = self;
[self addSubview:myTableView];
UIImageView *imgView = [[[UIImageView alloc] initWithFrame:CGRectMake(11, 50, 261, 4)] autorelease];
imgView.image = [UIImage imageNamed:@"top.png"];
[self addSubview:imgView];
imgView = [[[UIImageView alloc] initWithFrame:CGRectMake(11, tableHeight+46, 261, 4)] autorelease];
imgView.image = [UIImage imageNamed:@"bottom.png"];
[self addSubview:imgView];
CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0.0, 10);
[self setTransform:myTransform];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = (UITableViewCell*) [tableView dequeueReusableCellWithIdentifier:@"ABC"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"ABC"] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.font = [UIFont boldSystemFontOfSize:14];
}
cell.text = [[data objectAtIndex:indexPath.row] description];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self dismissWithClickedButtonIndex:0 animated:YES];
[self.caller didSelectRowAtIndex:indexPath.row withContext:self.context];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [data count];
}
-(void)dealloc{
self.data = nil;
self.caller = nil;
self.context = nil;
[myTableView release];
[super dealloc];
}
@end
Hi Jake,
Thanks for the solution. I am just a beginner and want to implement a alert as you have proposed.
I tried with your sample code. But I don’t know how to init the alert view and show it. Please if possible post me the code which utilizes this custom alert view and shows it.
Thanks in advance.
Raj
Just out of curiosity using this alertview was your app approved by the App Store? I hear they’re being rather strict on what type of hackery they allow.
Hi Jake,
Like Raj, I m in trouble = I can not manage to use your code ![]()
it s crashing when I init the AlertTableView (client side ) ! My fault, for sure =)
So could you post or send me a little example with a nsarray ?
many ( you can not count all of them
thanks in advance !!
[...] here are part 1 and part 2 with sample project of how to do exactly [...]
great stuff
i didn’t think of adding images over top and bottom
nice work
to init/call use something link this
AlertTableView *dcListAlert = [[AlertTableView alloc] initWithCaller:self data:allSteps title:@”Double Check List” andContext:nil];
[dcListAlert show];
[dcListAlert release];
be sure to add AlertTableViewDelegate in caller interface
and
-(void)didSelectRowAtIndex:(NSInteger)row withContext:(id)context {
NSLog([NSString stringWithFormat:@"Index %d clicked", row]);
}
in implementation of caller
Your code is broken when you have less than 6 elements in your dataset.
I am having trouble implimenting your code. I keep getting issues with the alerttableview.m
The error says ‘lt’ undeclared.
Does anyone know a solution?
The lt is undeclared error occurs because < is actually a typo caused by HTML and your internet browser.
It should be the less than symbol.
Hi great examples !!! I try hard to find a way to put an image, (logo company) at the top of my tableview. But I didn’t find any solution. How did U managed to do it ?
(I don’t have any view, just a tableview, so addsubview doesn’t work)
Thanks.
Thanks for the code!
Hi! Great article! We use the idea for the bottom and top images to make a better looking textfields on our alerts. Any change you have Retina display enabled versions of them ?
Thank you
Great code. With Andy’s tips to fix table height for less than 6 elements and with some fix into code (deprecated methods and ‘<' character) it works fine!
Thanks a lot!
April 4, 2009 at 9:22 am
Hi. Thanks for the code. Could you provide the top image you created, with rounded corners and a dropshadow?
April 24, 2009 at 2:08 pm
Here are links to the two images:
Top
Bottom
July 15, 2009 at 2:44 pm
Hi, the code works perfect, you saved me a lot of time.
thanks,
sven
February 17, 2010 at 7:42 am
Hey I copied the code and tried to run it but it gives me this error:
“request for member ‘view’ is something not a structure or union.”
Anybody know what that means. Please!!!