display done button on UIPickerview

I have written the following code in the viewDidLoad method:

categoryPickerView=[[UIPickerView alloc]init];
categoryPickerView.alpha = 0;
[self.view addSubview:categoryPickerView];
categoryPickerView.delegate=self;
categoryPickerView.tag=1;

and called this method to hide picker view

- (IBAction)hidePickerView:(id)sender {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.6];
    CGAffineTransform transfrom = CGAffineTransformMakeTranslation(0, 200);
    categoryPickerView.transform = transfrom;
    categoryPickerView.alpha = categoryPickerView.alpha * (-1) + 1;
    [UIView commitAnimations];
}

My problem is that I want to display a "Done" button on a picker view and the picker view should hide on button click.


You can use this code,

UIToolbar *toolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,320,44)];
[toolBar setBarStyle:UIBarStyleBlackOpaque];
UIBarButtonItem *barButtonDone = [[UIBarButtonItem alloc] initWithTitle:@"Done" 
    style:UIBarButtonItemStyleBordered target:self action:@selector(changeDateFromLabel:)];
toolBar.items = @[barButtonDone];
barButtonDone.tintColor=[UIColor blackColor];
[pickerView addSubview:toolBar];
//(or)pickerView.inputAccessoryView = toolBar;

and set button action method for changeDateFromLabel:

-(void)changeDateFromLabel:(id)sender
{
   [[your UI Element] resignFirstResponder];
}

You can create view and add toolbar with "Done" button and UIPickerView as subviews

- (void)createInputView {
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;

    UIToolbar *toolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 44)];
    [toolBar setBarStyle:UIBarStyleDefault];
    UIBarButtonItem *flex = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];

    UIBarButtonItem *barButtonDone = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                                                  style:UIBarButtonItemStyleBordered
                                                                 target:self
                                                                 action:@selector(doneClicked)];
    toolBar.items = @[flex, barButtonDone];
    barButtonDone.tintColor = [UIColor blackColor];

    UIPickerView *picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, toolBar.frame.size.height, screenWidth, 200)];
    picker.delegate = self;
    picker.dataSource = self;
    picker.showsSelectionIndicator = YES;


    UIView *inputView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, toolBar.frame.size.height + picker.frame.size.height)];
    inputView.backgroundColor = [UIColor clearColor];
    [inputView addSubview:picker];
    [inputView addSubview:toolBar];

    textField.inputView = inputView;
}

- (void)doneClicked {
    [textField resignFirstResponder];
}

You need to use the UIToolbar as the accessory view: Try with this:

#pragma mark - PickerView for Location Selection

- (UIPickerView *)locationsPicker {
    if ( locationsPicker == nil ) {
        locationsPicker = [[UIPickerView alloc] init];
        locationsPicker.delegate = self;
        locationsPicker.dataSource = self;
        locationsPicker.showsSelectionIndicator = YES;
    }
    return locationsPicker;
}

- (UIToolbar *)accessoryView {
    if ( accessoryView == nil ) {
        accessoryView = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];

        UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:
                                       UIBarButtonSystemItemDone
                                       target:self
                                       action:@selector(onLocationSelection)];
        [accessoryView setItems:[NSArray arrayWithObject:doneButton]];
    }
    return accessoryView;
}

- (void)onLocationSelection {
    NSInteger row = [self.locationsPicker selectedRowInComponent:0];
    if ( [Location isFirstResponder] ) {
       NSLog(@"%@", [listOfLocations objectAtIndex:row]);
        [Location resignFirstResponder];
    }
}

Copy-pasteable solution!

This works in iOS 10 (and probably other versions too) I wrote this code on 9/4/2017.

I needed to combine other answers to get what I wanted, which was a Cancel button, Done button, and a picker view that would show/hide on the tap of a button. Ignore the fact that my screenshot only has one item in the picker.. I only have one item in my array. I've tested multiple items, it works fine.

enter image description here

Disclaimer! I have tested and used this code. I generalized the names in my example, so I apologize in advance if I missed one and they property names don't line up.

In order to have a pickerView that appears on the tap of a button, you need to create a dummyTextField (of type UITextField anywhere in your view controller. This is because UIPickerView's are designed to work with UITextFields. To mimic showing and hiding of the picker, your button taps need to call the dummyTextField's becomeFirstResponder and resignFirstResponder methods. (examples are below)

Step one

Create a custom data source class. MyObj is the class you want to have items show up in the picker for.

In .h:

#import <UIKit/UIKit.h>
@class myObj;

@interface PickerDataSource : NSObject <UIPickerViewDataSource, UIPickerViewDelegate>
@property (nonatomic, strong) MyObj *selectedObject;
@end

In .m:

#import "PickerDataSource.h"
#import "MyObj.h"

@implementation PickerDataSource

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    self.selectedObject = mySourceArray[row];
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return mySourceArray.count;
}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 1;
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    return ((MyObj *)mySourceArray[row]).myTitle;
}

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
    int sectionWidth = 300;
    return sectionWidth;
}

@end

Step two In your view controller, import custom data source, delegate, and set properties: (You must include the text field delegate!)

#import "PickerDataSource.h"

@interface MyViewController () <UIPickerViewDelegate, UITextFieldDelegate>
@property (strong, nonatomic) PickerDataSource *pickerDataSource;
@property (strong, nonatomic) UIPickerView *picker;
@property (strong, nonatomic) UITextField *dummyTextField;
@end

Step three

In your viewDidLoad, call [self setupPicker]; (you'll create this method next)

Step four

Create setupPicker

- (void)setupPicker {
    // init custom picker data source
    self.pickerDataSource = [PickerDataSource new];
    // init custom picker
    self.picker = [UIPickerView new];
    // set the picker's dataSource and delegate to be your custom data source
    self.picker.dataSource = self.pickerDataSource;
    self.picker.delegate = self.pickerDataSource;
    self.picker.showsSelectionIndicator = YES;

    // next step is to write this configure method getting called here
    [self configurePickerSubviews];

    // lastly, add the dummyTextField to your view.
    [self.view addSubview:self.dummyTextField];
}

Step five

Create configurePickerSubviews

- (void)configurePickerSubviews {
    // A UIPickerView must be added as an inputView to a UITextField in order to be displayed on button tap
    // So you need to create a dummyTextField to do so.
    self.dummyTextField = [UITextField new];

    // Create a toolbar to add a done button
    UIToolbar *toolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width,44)];
    [toolBar setBarStyle:UIBarStyleDefault];
    UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:self action:@selector(locationPressed)];

    // Create a flex space so that done button will be right aligned
    UIBarButtonItem *flex = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(dismissPicker)];
    toolBar.items = @[cancel, flex, done];
    done.tintColor = [UIColor blackColor];

    [self.picker addSubview:toolBar];

    // Create an input view to add picker + done button as subviews
    UIView *inputView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, self.picker.frame.size.height + 44)];
    [self.picker setFrame:CGRectMake(0, 0, inputView.frame.size.width, inputView.frame.size.height)];
    inputView.backgroundColor = [UIColor clearColor];
    [inputView addSubview:self.picker];
    [inputView addSubview:toolBar];

    // Set custom inputView as container for picker view
    self.dummyTextField.inputView = inputView;

    // Hiding the textfield will hide the picker
    [self.dummyTextField setHidden:YES];
}

Step six

Configure the PickerDataSource so that it is fetching the data you want to display from your source array.

Step 7

Click Run!