Recently by Andrew Homeyer
Have you ever tried to use a UIActionSheet to display more than just buttons? It doesn't work, as you have probably figured out. The common solution is to create a custom UIViewController and display it using UIViewController's presentModalViewController, which can give you a nice slide up transition, much like how a UIActionSheet appears.
But that solution is missing something. A UIActionSheet slides up, but not all the way, leaving a semi-transparent covering over the parent view. Let's try to display a custom view with this same effect.
Full sample code can be found on github.
Let's start fresh by creating a new project in Xcode and choosing "Navigation-based Application." This gives us a RootViewController and an AppDelegate.
1. Create our custom action sheet
Create a new UIViewController subclass with XIB (right click on Classes -> Add -> New File). I'm naming it CustomUIActionSheetViewController.
Add an outlet in the parent view controller (RootViewController) for our action sheet and synthesize it.
@property(nonatomic, retain) IBOutlet CustomUIActionSheetViewController *customUIActionSheetViewController;
Open the RootViewController.xib in Interface Builder, drag a View Controller onto the document window. Tell it to be the CustomUIActionSheetViewController we just created by changing the Class property in the Identity Inspector, and the NIB Name in the Attributes Inspector.
Connect the outlet we created from the File's Owner to the controller.
At this point we've created our action sheet view controller and given the RootViewController a reference to it. You won't see anything different from a new project when you build and run.
2. Show our custom action sheet
In the parent view controller (RootViewController), create a method to show the custom view.
- (void)showCustomView {
[self.navigationController.view
addSubview:self.customUIActionSheetViewController.view];
[self.customUIActionSheetViewController viewWillAppear:NO];
}
We're simply adding the view as a subview, we'll do the work to slide up the content in our custom view controller. Note that we need to call viewWillAppear ourselves, it's called for you when using presentModalViewController.
Add a button when the RootViewController first loads to show our custom action sheet.
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAction
target:self
action:@selector(showCustomView)] autorelease];
}
At this point, when you click the button in the navigation bar, you should see the custom view immediately appear without any transition.
3. Make background semi-transparent
When the custom view loads, we want the parent view to be covered by a semi-transparent view.
Open our custom action sheet NIB (CustomUIActionSheetViewController.xib) in Interface Builder.
In the Attributes Inspector for the UIView (there should only be one right now), click the background field. Change it's color to black (or the darkTextColor property), and change the Opacity to 50%.

When you build and run and click the right navigation button you should now see the semi-transparent view cover the parent view.
4. Add a view to hold our custom fields
Drag a new UIView onto the NIB and attach it to a new outlet in View Controller. I'm calling it the actionSheetView.
@property(nonatomic, retain) IBOutlet UIView *actionSheetView;
Adjust the height of the view to be less than 480, but keep the width at 320. This will determine how far up we'll slide the view.
Next, let's add a navigation bar and a 'done' button that will dismiss the dialog.
Drag a Navigation Bar onto the view, and then a Navigation Item onto the Navigation Bar. Change the Navigation Item's title to 'Done' and change the Navigation Bar's title to whatever you want.
Let's go ahead and add a stub in the the CustomUIActionSheetViewController's header for 'slideOut', make it a UIAction, and connect the done button to it in IB.
- (IBAction) slideOut;
At this point, when you click the right nav button, you won't see the view we just created because we haven't added it as a subview yet.
5. Add the slide up transition
Create a new method to slide up the actionSheetView, and call it from the viewWillAppear method.
- (void)viewWillAppear:(BOOL)animated {
[self slideIn];
}
- (void)slideIn {
//set initial location at bottom of view
CGRect frame = self.actionSheetView.frame;
frame.origin = CGPointMake(0.0, self.view.bounds.size.height);
self.actionSheetView.frame = frame;
[self.view addSubview:self.actionSheetView];
//animate to new location, determined by height of the view in the NIB
[UIView beginAnimations:@"presentWithSuperview" context:nil];
frame.origin = CGPointMake(0.0,
self.view.bounds.size.height - self.actionSheetView.bounds.size.height);
self.actionSheetView.frame = frame;
[UIView commitAnimations];
}
To do this, we change the frame of the actionSheetView so that its top is at the bottom of the screen, and then start an animation block. In the animation we change the frame again, this time setting it's top to be the height of the actionSheetView. When we commit the animation, the view will transition from it's current position (at the bottom) to the position defined in the animation block.
When you build and run you should see the actionSheetView slide up when you click the right nav button.
6. Add the slide out transition
To slide out the custom view when 'done' is clicked we need to implement the stub we created earlier.
- (void) slideOut {
[UIView beginAnimations:@"removeFromSuperviewWithAnimation" context:nil];
// Set delegate and selector to remove from superview when animation completes
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
// Move this view to bottom of superview
CGRect frame = self.actionSheetView.frame;
frame.origin = CGPointMake(0.0, self.view.bounds.size.height);
self.actionSheetView.frame = frame;
[UIView commitAnimations];
}
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if ([animationID isEqualToString:@"removeFromSuperviewWithAnimation"]) {
[self.view removeFromSuperview];
}
}
Like before, we're going to change the origin of the frame wrapped in an animation block to slide out the view. We're doing a little extra work with setAnimationDidStopSelector. When the animation finishes, we want to remove our entire view (including the semi-transparent view), to go back to the state we were in before it was slid in. In the callback selector we simply use removeFromSuperview to do this.
That's it! The actionSheetView should slide in when you click the right navigation button, and the done button should slide it out.
Thanks to this answer on Stack Overflow for getting me started.
Download or fork the sample code from github.

