If you're new here, you may want to subscribe to my RSS feed or follow me on Twitter. Thanks for visiting!
This is the second part of a three-part series to help get iPhone Developers up-to-speed with iPad development by focusing on three of the most interesting new classes (at least to me): UISplitView, UIPopoverController, and Custom Input Views.
In the first part of the series, we made an app with a split view that displays a list of monsters on the left side, and details on the selected monster on the right side.
In this second installment, we’re going to try out popovers view with a simple example: we’ll add a popover to let the user select from a list of colors to change the color of the monster’s name. (Jump to Part 3 in the series.)
We’ll start out with where we left off the project last time, so grab a copy if you don’t have it already.
Creating our Color Picker
Let’s start by creating the view that we’ll use to let the user pick between a list of colors. We’ll make this a simple table view with a list of color names.
So go to “File\New File…”, pick “UIViewController subclass”, and make sure “Targeted for iPad” and “UITableViewController subclass” are checked but “With XIB for user interface” is NOT checked, and click Next. Name the class ColorPickerController, and click Finish.
Then replace ColorPickerController.h with the following:
@protocol ColorPickerDelegate - (void)colorSelected:(NSString *)color; @end @interface ColorPickerController : UITableViewController { NSMutableArray *_colors; id<ColorPickerDelegate> _delegate; } @property (nonatomic, retain) NSMutableArray *colors; @property (nonatomic, assign) id<ColorPickerDelegate> delegate; @end |
Here we declare a delegate so that this class can notify another class when a user selects a color. We then declare two variables/properties: one for the list of colors to display, and one to store the delegate itself.
Then make the following changes to ColorPickerController.m:
// Under @implementation @synthesize colors = _colors; @synthesize delegate = _delegate; // Add viewDidLoad like the following: - (void)viewDidLoad { [super viewDidLoad]; self.clearsSelectionOnViewWillAppear = NO; self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0); self.colors = [NSMutableArray array]; [_colors addObject:@"Red"]; [_colors addObject:@"Green"]; [_colors addObject:@"Blue"]; } // in numberOfSectionsInTableView: return 1; // in numberOfRowsInSection: return [_colors count]; // In cellForRowAtIndexPath, under configure the cell: NSString *color = [_colors objectAtIndex:indexPath.row]; cell.textLabel.text = color; // In didSelectRowAtIndexPath: if (_delegate != nil) { NSString *color = [_colors objectAtIndex:indexPath.row]; [_delegate colorSelected:color]; } // In dealloc self.colors = nil; self.delegate = nil; |
Most of this should be normal table view stuff except for the following line:
self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0); |
This line sets the size of how large the popover should be when it is displayed. If you do not add this line, by default the popover will be the entire height of the screen (which is usually too large).
Displaying the Picker
Believe it or not, that was the hardest part. Now to display the picker, all we need to do is add a button to our toolbar, and a little bit of code to display it and handle the selection.
So first, let’s add the button. Open up RightViewController.xib and add a Bar Button Item to the toolbar. Set the title of the button “Set Color”.
Now let’s declare a method for the button to trigger in RightViewController.h and declare a few variables we’ll need in a minute:
// Up top, under #import #import "ColorPickerController.h" // Modfiy class declaration @interface RightViewController : UIViewController <MonsterSelectionDelegate, UISplitViewControllerDelegate, ColorPickerDelegate> { // Inside class ColorPickerController *_colorPicker; UIPopoverController *_colorPickerPopover; // In property section @property (nonatomic, retain) ColorPickerController *colorPicker; @property (nonatomic, retain) UIPopoverController *colorPickerPopover; - (IBAction)setColorButtonTapped:(id)sender; |
Before we forget, go ahead and connect the action method to the Bar Button Item in Interface Builder by control-dragging from the Bar Button Item to File’s Owner and connecting to the “setColorButtonTapped” outlet.
Then let’s finish by making the required changes to RightViewController.m:
// In synthesize section @synthesize colorPicker = _colorPicker; @synthesize colorPickerPopover = _colorPickerPopover; // In dealloc self.colorPicker = nil; self.colorPickerPopover = nil; // Add to end of file - (void)colorSelected:(NSString *)color { if ([color compare:@"Red"] == NSOrderedSame) { _nameLabel.textColor = [UIColor redColor]; } else if ([color compare:@"Green"] == NSOrderedSame) { _nameLabel.textColor = [UIColor greenColor]; } else if ([color compare:@"Blue"] == NSOrderedSame){ _nameLabel.textColor = [UIColor blueColor]; } [self.colorPickerPopover dismissPopoverAnimated:YES]; } - (IBAction)setColorButtonTapped:(id)sender { if (_colorPicker == nil) { self.colorPicker = [[[ColorPickerController alloc] initWithStyle:UITableViewStylePlain] autorelease]; _colorPicker.delegate = self; self.colorPickerPopover = [[[UIPopoverController alloc] initWithContentViewController:_colorPicker] autorelease]; } [self.colorPickerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } |
Ok let’s explain this a bit. All popovers are is a “wrapper” around an existing view controller that “floats” it in a certain spot and possibly displays an arrow showing what the popover is related to. You can see this in setColorButtonTapped – we create our color picker, and then wrap it with a popover controller.
Then we call a method on the popover controller to display it in the view. We use the helper function presentPopoverFromBarButtonItem to display the popover.
When the user is done, they can tap anywhere outside the popover to dismiss it automatically. However if they select a color, we also want it to be dismissed, so we call the dismissPopoverAnimated method to get rid of the popover on-demand (as well as setting the color appropriately).
And that’s it! Compile and run and when you tap the “Set Color” bar button item, you should see a popover like the following that changes the label color:
You will find yourself using popovers quite a bit in places where users need to edit a field or toggle a setting, rather than the iPhone style where you navigate to the next level in a UINavigationController. They call this “flattening the hierarchy” in the iPad docs.
Show Me the Code!
Here’s a copy of all of the code we’ve developed so far.
Check out the next part of the series, where we cover how to use custom input views on the iPad!
Category: iPad








Great info here, thanks for sharing. Could you explain how to release the popover view from a round rect button inside the popover view?
Basically, popover loads up, button inside the popover view is pressed and presents something modally (or whatever) while dismissing the popover.
Thanks for sharing your knowledge!
@Dan: For this situation, here’s what I usually do:
1) There is a parent view controller that presents the popover controller.
2) There is a child view controller that contains the button.
3) The child view controller declares a “ChildViewControllerDelegate” protocol, with a method such as “popoverClosed”, and has a member variable for a delegate.
4) The parent view controller implements the delegate, and when it creates the child view controller it sets itself as the delegate.
5) When the button is tapped, the child view controller calls the method on its delegate. This ends up calling a method in the parent view controller, which can then dismiss the popover.
Hope this helps!
how do I pass an array to to popover ?
Never mind, I got it
@Nik: Hehe, w00t! :]
What a GREAT website. I love it all. I have been a Microsoft programmer since DOS 5.0 and that is a long time. I bought my IPAD and MacBook Pro 3 weeks ago and I have not booted the PC since. You have saved me a lot of time getting started, much more than than the two books I purchased.
Look for my Donation, (under a different email name).
Thanks, Richard.
@Richard: I sent you an email, but thanks again for the kind donation!! Yeah I don’t use my PC anymore either, heh… which is a good thing as it keeps me from playing as many games I guess! :]
So what made you decide to start programming for the iPad?
I really like the form factor, touch interface, shape and feel of the IPad. Also it is a platform that I can get started on early in its lifecycle for development. I am capturing the things I am learning in a project that will help me remember how to do stuff. I will send it to you when it gets further along.
Richard.
@Richard: Yeah, I really enjoy the iPad as well, it’s a great combination of technologies and there are tons of cool apps you can make for it.
Regarding your project: awesome, looking forward to it!
hi ray and thank you for your tutorial, is it possible to use popover for iphone ? i tried but it crashed!
Nice tut @Ray. I’ve working with UIPopoverController and it’s amazing, but now i want to change their color :(. I read de interface definition and they have a view for the popover but it’s private. Would you mind share something about building a custom popover, from Zero? thanks, nice blog.
@Ali: Popovers are only available on the iPad, so you’ll need to use an alternate method on the iPhone.
@Jason: Thanks, added to the idea list! But to get you pointed in the right direction, you can just create a UIView the way you want it then add it to your parent view at a set location. The trickiest bit about the popover code would be the “smarts” to put the popover in the right location based on the window size and where you’ve asked to place it…
Thanks again Ray.
Slightly off-topic question about properties… in setColorButtonTapped, you have this code:
if (_colorPicker == nil) {
self.colorPicker = [[[ColorPickerController alloc]
initWithStyle:UITableViewStylePlain] autorelease];
_colorPicker.delegate = self;
self.colorPickerPopover = [[[UIPopoverController alloc]
initWithContentViewController:_colorPicker] autorelease];
}
You seem to go back and forth between using
_colorPickerandself.colorPicker. Is there a reason for that?@Hugh: Yeah I definitely need to post an article about this someday as it’s a common question, but in the meantime check out my reply to @Jason in the following tutorial:
http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial
As an addendum to that, I usually use the property for setting, and the instance variable for reading, personal preference.
Hi, just a question.
In a splitviewcontroller, if I want to change the values in the tableview of the leftviewcontroller, if the iPad is in portrait mode values are updated. If iPad is on landscape mode, I have to rotate it so that values can change.
Is there any way to force the update of leftviewcontroller data on landscape mode? Regards.
Can we use UIPopoverController for popup on a Rounded Rectangle button. Your response is highly appreciated.
@Gianiuca: The best way to handle that is to change the data model when your values change, whether or not the table view is visible, and then in your table view’s viewWillAppear, reload the table view. Then the table view will refresh itself based on the updated model.
@Jeff: Yep, you can use a UIPopoverController pretty much anywhere you want. But instead of using presentPopoverFromBarButtonItem, you’d use presentPopoverFromRect.
Hi Ray,
Again a great lession.
I am facing a prob here . Everything works good and great when I select any color the required change happens but when I clicked the set color button next time popover doesnt comes up . Can u plz tell whats the prob.
Many thanks
OMG I’ve been fighting for a day trying to do this… because I was trying to reuse the same UIPopoverController for the results list (the dynamic one create in the UISplitviewController delegate) and my own popover. You use a separate one which will probably solve all my issues!
I feel stupid now! lol
Useful contribution though: I learned in the WWDC videos that as of iOS 3 you can now drop the field definition (e.g. ColorPickerController *_colorPicker;) and simply use the @property declaration (+@synthesize of course). @property (nonatomic, retain) ColorPickerController *colorPicker;
I find it keeps the .h files that much cleaner!
Cheers!
Anyone know how to change border color of UIPopoverController?
@Binod: Did you try comparing your code to the sample project?
@OB: You can reuse popovers, but it’s often simpler to just create new ones heh. Cool tip about dropping the instance vars, good to know!
@Itgbau: I don’t but if you figure it out let me know hehe :]
hai ray..
i’m really newbie in ipad and objective C programming
i have already try your tutorial,
but i have a problem with showing the picker in the popover
when i click the popover, the popover show 3 line just like your picture, but there are no word written in that popover, but if i choose one the 1st line, the color of the monster name change to be red
actually there are no errors in the logic of source code, but it’s annoyed me,
when i print out the value of bar button using
NSLog (@”show color at click bar button %@”, _colorpicker.title);
the value is null
can somebody help me??
@risma: So you’re saying that no text is showing up in the table view that displays? Make sure you added the code in cellForRowAtIndexPath to set the text label properly for each row. If all else fails, have you tried comparing your code to the sample project?
Thanks so much for the discussion! I’m using multiple buttons on the toolbar that each call a popover. I’m using separate instances of UIPopover for each button. Is that what you’d recommend? Works well but when I dismiss one instance from another, I’m getting an extra release in there. Do you happen to have a tutorial with multiple popovers?
Thanks Again!
Jon
@Jon: Yeah, personally I like to have a different popover class per button, I find it easier than having to worry about views resizing. Not sure why you’d be getting an extra release though, can you post the code where you create a popover instance (and show the declaration of the property you’re using to store the popover too?)
I’ve implemented a passcode lock for my app which works great on the iPhone, but now I’m running into trouble on the iPad. I’m after something like what they’re doing in the Dropbox app. As near as I can tell they’re using a popover for the keypad and display, with the arrow direction set to zero (to hide the arrows). Unfortunately the Apple docs don’t say whether setting the arrow direction to 0 is allowed. Is that a valid value?
@Andrew: Hm, I don’t know, I haven’t tried! I don’t see why they wouldn’t though, because you could just simulate the look of a non-arrowed popover by just adding a subview that looks like a popover at a particular frame.
Hello Ray, wonderful tutorial. but can you tell me that rather than tapping and then showing popovercontroller, what i want , when application launches popovercontroller automatically appear rather than clicking on some controll and it appears.
Thanks,
Asif Noor
@asef: You can call presentPopoverFromRect any time you’d like to show a popover, even when a view controller loads (perhaps in viewDidLoad for example).
I love you! You made my day!
@Carlo: Lol thanks! :]
Ray, have you ever worked with an UIPopoverController in Cocos2d? That’s what I’m about to embark on….
Your tutorials are without a doubt the best out there. Do you have a book out? If not, you should consider it.
I have a button I created programatically. I change the position of the button when the iPad rotates from portrait to landscape (in the shouldAutorotateToInterfaceOrientation method of my view controller).
I am creating a popover based on the rect of my button:
[self.popoverController presentPopoverFromRect:editTeamButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
It works well in either portrait or landscape (the popover appears and points at the button), but when the popover is showing while I rotate, the popover doesn’t move as the button moves. What is the best way to adjust the position of the popover in this case?