Add UIPickerView & a Button in Action sheet - How?
Solution 1
Update for iOS 7
Apple docs for UIActionSheet: UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy
I recommend against trying to customize the contents of an ActionSheet, as it can lead to serious invalid context errors in iOS 7. I just spent a few hours working through this problem and ultimately decided to take a different approach. I replaced the call to show the action sheet with a modal view controller containing a simple tableview.
There are many ways to accomplish this. Here's one way that I just implemented in a current project. It's nice because I can reuse it between 5 or 6 different screens where I all users to select from a list of options.
- Create a new UITableViewController subclass,
SimpleTableViewController
. - Create a UITableViewController in your storyboard (embedded in a navigation controller) and set its custom class to SimpleTableViewController.
- Give the navigation controller for SimpleTableViewController a Storyboard ID of "SimpleTableVC".
- In SimpleTableViewController.h, create an NSArray property that will represent the data in the table.
- Also in SimpleTableViewController.h, create a protocol
SimpleTableViewControllerDelegate
with a required methoditemSelectedatRow:
, and a weak property called delegate of typeid<SimpleTableViewControllerDelegate>
. This is how we will pass the selection back to the parent controller. - In SimpleTableViewController.m, implement the tableview data source and delegate methods, calling
itemSelectedatRow:
intableView:didSelectRowAtIndexPath:
.
This approach has the added benefit of being fairly reusable. To use, import the SimpleTableViewController class in your ViewController.h, conform to the SimpleTableViewDelegate, and implement the itemSelectedAtRow:
method. Then, to open the modal just instantiate a new SimpleTableViewController, set the table data and delegate, and present it.
UINavigationController *navigationController = (UINavigationController *)[self.storyboard instantiateViewControllerWithIdentifier:@"SimpleTableVC"];
SimpleTableViewController *tableViewController = (SimpleTableViewController *)[[navigationController viewControllers] objectAtIndex:0];
tableViewController.tableData = self.statesArray;
tableViewController.navigationItem.title = @"States";
tableViewController.delegate = self;
[self presentViewController:navigationController animated:YES completion:nil];
I create a simple example and posted it on github.
Also see Showing actionsheet causes CGContext invalid context errors.
Solution 2
One more solution:
-
no toolbar but a segmented control (eyecandy)
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]; [actionSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent]; CGRect pickerFrame = CGRectMake(0, 40, 0, 0); UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:pickerFrame]; pickerView.showsSelectionIndicator = YES; pickerView.dataSource = self; pickerView.delegate = self; [actionSheet addSubview:pickerView]; [pickerView release]; UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Close"]]; closeButton.momentary = YES; closeButton.frame = CGRectMake(260, 7.0f, 50.0f, 30.0f); closeButton.segmentedControlStyle = UISegmentedControlStyleBar; closeButton.tintColor = [UIColor blackColor]; [closeButton addTarget:self action:@selector(dismissActionSheet:) forControlEvents:UIControlEventValueChanged]; [actionSheet addSubview:closeButton]; [closeButton release]; [actionSheet showInView:[[UIApplication sharedApplication] keyWindow]]; [actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
Solution 3
Even though this question is old, I'll quickly mention that I've thrown together an ActionSheetPicker class with a convenience function, so you can spawn an ActionSheet with a UIPickerView in one line. It's based on code from answers to this question.
Edit: It now also supports the use of a DatePicker and DistancePicker.
UPD:
This version is deprecated: use ActionSheetPicker-3.0 instead.
Solution 4
Yep ! I finally Find it.
implement following code on your button click event, to pop up action sheet as given in the image of question.
UIActionSheet *aac = [[UIActionSheet alloc] initWithTitle:@"How many?"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
UIDatePicker *theDatePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
if(IsDateSelected==YES)
{
theDatePicker.datePickerMode = UIDatePickerModeDate;
theDatePicker.maximumDate=[NSDate date];
}else {
theDatePicker.datePickerMode = UIDatePickerModeTime;
}
self.dtpicker = theDatePicker;
[theDatePicker release];
[dtpicker addTarget:self action:@selector(dateChanged) forControlEvents:UIControlEventValueChanged];
pickerDateToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
pickerDateToolbar.barStyle = UIBarStyleBlackOpaque;
[pickerDateToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(DatePickerDoneClick)];
[barItems addObject:doneBtn];
[pickerDateToolbar setItems:barItems animated:YES];
[aac addSubview:pickerDateToolbar];
[aac addSubview:dtpicker];
[aac showInView:self.view];
[aac setBounds:CGRectMake(0,0,320, 464)];
Solution 5
Marcio's excellent solution to this question was of great help to me in adding subviews of any kind to a UIActionSheet.
For reasons that are not (yet) entirely clear to me, the bounds of the UIActionSheet can only be set after it has been displayed; both sagar's and marcio's solutions successfully address this with a setBounds:CGRectMake(...) message being sent to the actionsheet after it is shown.
However, setting the UIActionSheet bounds after the sheet has been displayed creates a jumpy transition when the ActionSheet appeaars, where it "pops" into view, and then only scrolls in over the final 40 pixels or so.
When sizing a UIPickerView after adding subviews, I recommend wrapping the setBounds message sent to the actionSheet inside an animation block. This will make the entrance of the actionSheet appear smoother.
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
// add one or more subviews to the UIActionSheet
// this could be a UIPickerView, or UISegmentedControl buttons, or any other
// UIView. Here, let's just assume it's already set up and is called
// (UIView *)mySubView
[actionSheet addSubview:myView];
// show the actionSheet
[actionSheet showInView:[UIApplication mainWindow]];
// Size the actionSheet with smooth animation
[UIView beginAnimations:nil context:nil];
[actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
[UIView commitAnimations];
Sagar Kothari
iOS App Developer, Android App Developer, ReactJS Developer, Flutter
Updated on May 21, 2020Comments
-
Sagar Kothari about 4 years
My application requires following things to be added in an action sheet.
- UIToolbar
- Button on UIToolbar
- UIPicker Control
I have included an image to understand my requirements.
Could you please explain, how this can be implemented?
-
Apache over 14 yearshi sagar, thanks for the code, DatePickerDoneClick button event, how to declare it thanks
-
Apache over 14 yearssagar, how to dismiss the pop up action sheet, and add selected value into the text field thanks
-
Sagar Kothari over 14 years[aac dimisswithclickedindex] // something like this method. ( see I have placed a done button in action sheet & add done button target - selector & in selector place dimissal code.
-
Apache over 14 yearsthanks for reply sagar, i add [aac dismissWithClickedButtonIndex]; but i'm getting warning: 'UIActionSheet' may not respond to '-dismissWithClickedButtonIndex' then i create new method for selector 'DatePickerDoneClick' as below - (void) DatePickerDoneClick { NSLog(@"Done clicked"); ratings.text = pickerView objectAtIndex:row] } what shall i do, so when i click the done button UIActionSheet(aac) dismiss and textfield(ratings.text) be filled with selected value from picker thanks sagar
-
Apache over 14 yearshi sagar, i able to update the text all, just can't dismiss the action sheet, even when click done button can trig, what shall i add into - (void) DatePickerDoneClick { NSLog(@"Done clicked"); NSInteger row = [pickerView selectedRowInComponent:0]; rat.text = [pickerData objectAtIndex:row]; } so action sheet dismiss thanks
-
Apache over 14 yearshi sagar, thanks, i'm able find to dismiss the action sheet, thanks
-
zs2020 almost 14 years@selector(DatePickerDoneClick) should be @selector(DatePickerDoneClick:)
-
Mahesh Babu over 13 yearsi am getting UIApplication may not respond to mainwindow warning and application terminates.
-
Namratha over 13 yearsI'm trying to use the UIPickerView but haven't been able to integrate it into my app. I want the UIPickerView to be displayed on click of a button whose action is registered in MYViewController:UIViewController. In the action method I have put the above code of pickerview. what else should I do? Please help.
-
Michael Morrison about 13 yearsSuper... using this in my app.
-
fredrik about 13 yearsI changed [actionSheet showInView:[UIApplication mainWindow]]; to make it work. [actionSheet showInView:self.view]; or [actionSheet showFromTabBar:self.tabBarController.tabBar]; if you have a tabbar
-
Eric G almost 13 yearsActually fredrik, you should use [[UIApplication sharedApplication] keyWindow]. Edited source to change that.
-
ceperry over 12 yearsThis is a great tip. Under ios 4 and later, this style of animation is "discouraged" though as per the docs. Under iOS 4 or later, try this instead: UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{[actionSheet setBounds:CGRectMake(0,0,320,485)];} completion:NULL];
-
Eshwar Chaitanya over 12 years@Namratha If you want to display the picker view on button click,The best solution in this link:stackoverflow.com/questions/8617164/… it will help you better :)
-
spybart about 12 yearsthanks for this post. the only thing I wasn't happy about was the use of UISegmentedController, which is not intended to be used as a single button (the result is a slightly blurry button on non-retina display). I was easily able to work around this by adding a toolbar with the done button at the top of the actionsheet, which works perfect.
-
jere almost 12 yearshas anyone used this in production code? no risk getting rejected?
-
vinothp almost 12 years@Sick do i need to add your name in my project if i use your code, thanks
-
vinothp almost 12 years@Spark is it possible to give some idea about getting the picker text.. thanks for your post+1
-
Kyle Clegg almost 12 years@VanDuTran I figured it out. see solution below.
-
huggie over 11 yearsThe ActionSheetPicker project mentioned by sickAnimations handles horizontal orientation just fine.
-
Isuru about 11 yearsHello, I'm using this class in my app and it works great. Thanks. I have a question. In the
ActionSheetDatePicker
mode, you can add multiple buttons to the toolbar on top. Is this possible with just normalActionSheetStringPicker
too? -
OpenUserX03 about 11 yearsWell if you read above you will see that this ActionSheetPicker class came about in the first place, due to this thread. So yeah that is the best way, thanks to the accepted solution (¬_¬). stackoverflow.com/questions/1262574/…
-
WINSergey almost 11 yearsVan Du Tran - (void)dismissActionSheet:(UISegmentedControl*)sender{ UIActionSheet actionSheet = (UIActionSheet)[sender superview]; [actionSheet dismissWithClickedButtonIndex:0 animated:YES]; }
-
marciokoko almost 11 years@Spark In order to dismiss it, I would have to make aac an ivar and call [_aac removeFromSuperview]; right?
-
Kyle Clegg almost 11 yearsCan it do multiple selection?
-
kevinl almost 11 yearsUIActionSheet *actionSheet = (UIActionSheet *)[(UIView *)sender superview]; [actionSheet dismissWithClickedButtonIndex:0 animated:YES]; might also work for dismissing the action sheet
-
ShimSham almost 11 yearsThis seem to be broken in iOS 7, it still works but Xcode gives a lot of runtime warnings like: "<Error>: CGContextSetFillColorWithColor: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update." Anyone have found a solution for this or is it Apple that doesn't like this solution and now want to get rid of it...?
-
Stu P. over 10 yearsLooks like apple is about to start enforcing their rule that actionsheets should not be subclassed as I'm getting this error message - " <Error>: CGContextSetFillColorWithColor: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update."
-
Kyle Clegg over 10 yearsHeads up: this causes many CGContext errors in iOS 7. See stackoverflow.com/questions/19129091/…
-
marcio over 10 years@Kyle I totally agree with you. This answers is really old and shows bad coding practices. sorry that
-
Sagar Kothari over 10 yearsAhh iOS 7! You ruined everything which was developed till now. :(
-
Kyle Clegg over 10 years@StuartP. see my answer
-
Daniel Sanchez over 10 years@Kyle Can you expand your answer by telling what was your approach? Thanks
-
Kyle Clegg over 10 years@DanielSanchez Updated with an alternative suggestion and code sample.
-
avance over 10 yearsThis is great thanks. Have you found this works fine with iOS 7?
-
aZtraL-EnForceR over 10 years+1 for great simple picker view that animated to screen. But you have an error in the code. The view backgroundTapButton spawns on top of picker view and there for blocks the picker view from user interaction. what u really mean to do was to make this view on the remaining screen space that the picker view doesn't already fill... (u can't make a view behind the picker view like you mean to )
-
Steve Moser over 10 yearsA much more elegant solution IMHO is to set your textfield's input view to UIPickerView and its accessory to UIToolbar. You can check out the QuickDialog project for an example.
-
Femina about 10 yearsIts breaking in ios 7.1 :(