The UIPopoverController class (for iPad) is a very neat way to present information to the user. In this blog, we’ll show how to use this class to present a popover view when the user touches a button. Let’s see how it works.
Start Xcode, click on “Create a new Xcode project,” and select the Single View Application template. Name the project PopoverDemo, and choose options as shown here:
Click Next, choose a location to save the project, and click Create.
We now have a single view application. Open ViewController.xib, and drag a UIButton to the view. Change its title to “Show Popover…” and also choose a color for the background of the view. When finished, the xib should look something like this:
In ViewController.h, make the following changes:
#import "PopoverViewController.h"
@interface ViewController : UIViewController
@property (nonatomic, strong) IBOutlet UIButton *btnShowPopover;
– (IBAction)showPopover:(UIButton *)sender;
@end
Notice that we’ve imported a view controller that does not yet exist: we’ll take care of that soon. We’ve also added an IBOutlet property for the button, as well as an action method. Wire up the property and method (in the ViewController.xib file) as shown:
We will now make a new view controller for our popover. In the navigator panel, select ViewController.xib, then right – click, and choose “New File…” Select the Objective-C Class template, click Next, name the new class PopoverViewController, and make sure that it is a subclass of UIViewController as shown:
Click Next, save the class in the default location, and click Create.
Now open PopoverViewController.xib, select the view, and delete it. (You cannot resize a view made by Xcode as a part of the view controller creation process.) Drag a new UIView from the library, and in the Size inspector, set the view’s properties as shown:
(The value of Height will not be used, the height of the popover will be configured by the system when it is displayed.)
Drag a Navigation Bar to the top of the view; also drag a UILabel onto the view. Set their texts as shown:
In this demo, we’ve also set the background color for the view. Now right – click the File’s Owner object, and drag from the circle to the right of “view” to the new View we’ve just created. This sets the View in interface builder as the PopoverViewController’s view property. (If we omit this step, the view controller cannot display the view!)
Open ViewController.m, and alter the file as shown:
@interface ViewController ()
{
PopoverViewController *controller;
UIPopoverController *popoverController;
}
@end
@implementation ViewController
@synthesize btnShowPopover;
– (IBAction)showPopover:(UIButton *)sender
{
if ([popoverController isPopoverVisible]) {
[popoverController dismissPopoverAnimated:YES];
} else {
//the rectangle here is the frame of the object that presents the popover,
//in this case, the UIButton…
CGRect popRect = CGRectMake(self.btnShowPopover.frame.origin.x,
self.btnShowPopover.frame.origin.y,
self.btnShowPopover.frame.size.width,
self.btnShowPopover.frame.size.height);
[popoverController presentPopoverFromRect:popRect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
– (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
controller = [[PopoverViewController alloc] initWithNibName:@"PopoverViewController" bundle:nil];
popoverController = [[UIPopoverController alloc] initWithContentViewController:controller];
}
– (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
@end
Notice that the PopoverViewController (controller) and UIPopoverController (popoverController) objects are declared as iVars in the @interface section of the .m file, rather than properties. These iVars will not be exposed to any other class, nor should they be: they are private to the implementation of ViewController.
Let’s look first at viewDidLoad:. In this method, we instantiate both controller and popoverController. The controller object is an instance of our PopoverViewController class, we initialize it here with the nib name of the xib file, though initializing it with a nib name of nil would work as well. (Why? See the end of the blog for the answer…) After the controller is instantiated, we initialize popoverController with its contentViewController property set to the controller object.
In the showPopover: method, we respond to a touch on the button. If the popoverController is already shown, it is dismissed. Otherwise, we call presentPopoverFromRect: inView: permittedArrowDirections: animated:. The rectangle to use here is the frame of the object that summons the popover; in this case, the frame of the UIButton object. (This is the reason we set an outlet property to the button.) It is a good idea to use UIPopoverArrowDirectionAny as the value of the permittedArrowDirections argument: this ensures that wherever the system displays the popover, its arrow will always point to the control that summoned it.
In this demo, we’re not making any changes to the PopoverViewController class. We could place a button in the PopoverViewController.xib that calls a delegate method back to ViewController to dismiss the popover, but in this case, we simply rely on the fact that when the user touches outside the popover, it will be dismissed.
Run the application in both portrait and landscape mode:
There is nothing in our code that sets the behavior of the popover, except passing it the information about the button’s frame. In any orientation, the arrow of the popover will always point to the button, and the popover’s height and position will be adjusted to the best fit.
As for the answer to the question “why would a nib name of nil work just as well?”, in this case we created the nib file for PopoverViewController at the same time we created the controller. Since the controller has a nib (PopoverViewController.xib), nil tells the compiler to use the default nib: the one the controller was already associated with.
Of course, if we want to associate multiple nibs with a single controller, we can do that as well, but that is the subject of another blog…