Add UIPickerView & a Button in Action sheet - How?

115,848

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.

  1. Create a new UITableViewController subclass, SimpleTableViewController.
  2. Create a UITableViewController in your storyboard (embedded in a navigation controller) and set its custom class to SimpleTableViewController.
  3. Give the navigation controller for SimpleTableViewController a Storyboard ID of "SimpleTableVC".
  4. In SimpleTableViewController.h, create an NSArray property that will represent the data in the table.
  5. Also in SimpleTableViewController.h, create a protocol SimpleTableViewControllerDelegate with a required method itemSelectedatRow:, and a weak property called delegate of type id<SimpleTableViewControllerDelegate>. This is how we will pass the selection back to the parent controller.
  6. In SimpleTableViewController.m, implement the tableview data source and delegate methods, calling itemSelectedatRow: in tableView: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.

animation

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]; 
Share:
115,848
Sagar Kothari
Author by

Sagar Kothari

iOS App Developer, Android App Developer, ReactJS Developer, Flutter

Updated on May 21, 2020

Comments

  • Sagar Kothari
    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.

    alt text

    Could you please explain, how this can be implemented?

  • Apache
    Apache over 14 years
    hi sagar, thanks for the code, DatePickerDoneClick button event, how to declare it thanks
  • Apache
    Apache over 14 years
    sagar, how to dismiss the pop up action sheet, and add selected value into the text field thanks
  • Sagar Kothari
    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
    Apache over 14 years
    thanks 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
    Apache over 14 years
    hi 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
    Apache over 14 years
    hi sagar, thanks, i'm able find to dismiss the action sheet, thanks
  • zs2020
    zs2020 almost 14 years
    @selector(DatePickerDoneClick) should be @selector(DatePickerDoneClick:)
  • Mahesh Babu
    Mahesh Babu over 13 years
    i am getting UIApplication may not respond to mainwindow warning and application terminates.
  • Namratha
    Namratha over 13 years
    I'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
    Michael Morrison about 13 years
    Super... using this in my app.
  • fredrik
    fredrik about 13 years
    I 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
    Eric G almost 13 years
    Actually fredrik, you should use [[UIApplication sharedApplication] keyWindow]. Edited source to change that.
  • ceperry
    ceperry over 12 years
    This 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
    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
    spybart about 12 years
    thanks 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
    jere almost 12 years
    has anyone used this in production code? no risk getting rejected?
  • vinothp
    vinothp almost 12 years
    @Sick do i need to add your name in my project if i use your code, thanks
  • vinothp
    vinothp almost 12 years
    @Spark is it possible to give some idea about getting the picker text.. thanks for your post+1
  • Kyle Clegg
    Kyle Clegg almost 12 years
    @VanDuTran I figured it out. see solution below.
  • huggie
    huggie over 11 years
    The ActionSheetPicker project mentioned by sickAnimations handles horizontal orientation just fine.
  • Isuru
    Isuru about 11 years
    Hello, 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 normal ActionSheetStringPicker too?
  • OpenUserX03
    OpenUserX03 about 11 years
    Well 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
    WINSergey almost 11 years
    Van Du Tran - (void)dismissActionSheet:(UISegmentedControl*)sender{ UIActionSheet actionSheet = (UIActionSheet)[sender superview]; [actionSheet dismissWithClickedButtonIndex:0 animated:YES]; }
  • marciokoko
    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
    Kyle Clegg almost 11 years
    Can it do multiple selection?
  • kevinl
    kevinl almost 11 years
    UIActionSheet *actionSheet = (UIActionSheet *)[(UIView *)sender superview]; [actionSheet dismissWithClickedButtonIndex:0 animated:YES]; might also work for dismissing the action sheet
  • ShimSham
    ShimSham almost 11 years
    This 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.
    Stu P. over 10 years
    Looks 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
    Kyle Clegg over 10 years
    Heads up: this causes many CGContext errors in iOS 7. See stackoverflow.com/questions/19129091/…
  • marcio
    marcio over 10 years
    @Kyle I totally agree with you. This answers is really old and shows bad coding practices. sorry that
  • Sagar Kothari
    Sagar Kothari over 10 years
    Ahh iOS 7! You ruined everything which was developed till now. :(
  • Kyle Clegg
    Kyle Clegg over 10 years
    @StuartP. see my answer
  • Daniel Sanchez
    Daniel Sanchez over 10 years
    @Kyle Can you expand your answer by telling what was your approach? Thanks
  • Kyle Clegg
    Kyle Clegg over 10 years
    @DanielSanchez Updated with an alternative suggestion and code sample.
  • avance
    avance over 10 years
    This is great thanks. Have you found this works fine with iOS 7?
  • aZtraL-EnForceR
    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
    Steve Moser over 10 years
    A 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
    Femina about 10 years
    Its breaking in ios 7.1 :(