I have a scene in which I want to use picker views to get the input for multiple text boxes. I want each picker view to have a toolbar with a "Done" button that dismisses the picker view. So far I have:
//toolbar with "Done" button for picker views
UIToolbar *pickerToolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,320,44)];
[pickerToolBar setBarStyle:UIBarStyleBlackOpaque];
UIBarButtonItem *barButtonDone = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(doneWithPicker:)];
pickerToolBar.items = [[NSArray alloc] initWithObjects:barButtonDone,nil];
barButtonDone.tintColor=[UIColor blackColor];
//set up picker views
_pickerOne = [[UIPickerView alloc] init];
_pickerOne.dataSource = self;
_pickerOne.delegate = self;
self.textFieldOne.inputView = self.pickerOne;
self.textFieldOne.inputAccessoryView = pickerToolBar;
What I need is some way for the doneWithPicker method to figure out which text field is currently being edited and call resignFirstResponder.
Use the text field delegate to identify which field is active. (Make sure to set yourself up as the delegate)
#implementation WhateverViewController
{
UITextField *activeTextField;
}
...
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeTextField = textField;
}
Then, in the method that is called when your done button is tapped:
[activeTextField resignFirstResponder];
Currently, I am able to have my keyboard appear when I use a text field in my form. However, I'm not able to hide it. So I always need to shutdown the app and to open it back to change pages. I know there is a way to have like a toolbar to close the Keyboard when you want. However, i don't manage to see how to have this toolbar appear.
However, one of my fields enable a DatePicker View as a Keyboard instead of the natural keyboard.
- (void) initializeTextFieldInputView {
UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectZero];
datePicker.datePickerMode = UIDatePickerModeDate;
datePicker.minuteInterval = 5;
datePicker.backgroundColor = [UIColor whiteColor];
[datePicker addTarget:self action:#selector(dateUpdated:) forControlEvents:UIControlEventValueChanged];
self.DateText.inputView = datePicker;
}
- (void) dateUpdated:(UIDatePicker *)datePicker {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd"];
self.DateText.text = [formatter stringFromDate:datePicker.date];
}
To hide a keyboard either [self.DateText resignFirstResponder] or [self.view endEditing:YES] would work.
To add a toolbar over the keyboard use [self.DateText setInputAccessoryView:yourToolBarView];
For my app, I've attempted to make a UIDatePicker appear when the textfield called time is clicked.
In my viewDidLoad, I initialize it like this:
- (void)viewDidLoad {
time.delegate = self;
[time setValue:[UIColor darkGrayColor] forKeyPath:#"_placeholderLabel.textColor"];
datepicker = [[UIDatePicker alloc] init];
datepicker.datePickerMode = UIDatePickerModeCountDownTimer;
dateformatter= [[NSDateFormatter alloc] init];
[dateformatter setDateFormat:#"HH:mm:ss"];
}
Then, in the textFieldShouldBeginEditing method, I have this:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if (textField == time){
button.hidden = NO;
datepicker.hidden = NO;
}
return NO;
}
However, my UIDatePicker still stays hidden, and I am sure the outlets are connected
remove this line from your code and check ,if your picker is placed in Xib.
datepicker = [[UIDatePicker alloc] init];
In textFieldShouldBeginEditing you need to set frame according to your need and than you have to add date picker to your view. for example
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if (textField == _time){
CGRect frame = datepicker.frame;
frame.origin.x = 100(for eg,set according to you);
frame.origin.y = 100(for eg,set according to you);
datepicker.frame = frame;
[self.view addSubview:datepicker];
button.hidden = NO;
datepicker.hidden = NO;
}
return NO;
}
remove after you done with this.
If I have a set up like so:
- (void)textFieldDidBeginEditing:(UITextField* )textField {
if (textField.tag == 603) {
datePicker = [[UIDatePicker alloc]init];
[datePicker setDatePickerMode:UIDatePickerModeDate];
[datePicker setDate:[NSDate date]];
[datePicker addTarget:self action:#selector(showDate:) forControlEvents:UIControlEventValueChanged];
textField.inputView = datePicker;
}
}
in the setDate method, can I reference which textField the UIPickerDate is the inputView of? That sounds confusing but like something like this:
- (void) showDate: (UIDatePicker*) myDatePicker {
NSDate* selected = [myDatePicker date];
NSString* date = [selected description];
myTextField = myDatePicker.view //I know this is not a property
}
kind of in the same sense that a UITapGestureRecognizer knows what view is calling it...
Explanation for Woz: I could but it would make the app less dynamic. This is a big form for the installation of medical devices. To create the form I store all the element properties in a dictionary (label, type (text, checkbox, segmented control), form section, etc.) and then each dict in an array in the proper order. I programatically build everything while in the array loop. I guess I could subclass UITextField...but you got to admit it would be nice to know which textfield initiated the inputView
I just subclassed UITextField and got it to work:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.textColor = [colorManager setColor:66.0 :66.0 :66.0];
self.font = [UIFont fontWithName:#"Helvetica" size:18.0];
self.backgroundColor = [UIColor whiteColor];
self.borderStyle = UITextBorderStyleRoundedRect;
self.returnKeyType = UIReturnKeyDone;
self.userInteractionEnabled = YES;
datePicker = [[UIDatePicker alloc]init];
[datePicker setDatePickerMode:UIDatePickerModeDate];
[datePicker setDate:[NSDate date]];
[datePicker addTarget:self action:#selector(setDate:) forControlEvents:UIControlEventValueChanged];
self.inputView = datePicker;
}
return self;
}
- (void) setDate : (UIDatePicker*) myDatePicker {
NSDateFormatter* myDateFormatter = [[NSDateFormatter alloc] init];
[myDateFormatter setDateFormat:#"MM/dd/yyyy"];
NSString* dateString = [myDateFormatter stringFromDate:myDatePicker.date];
self.text = dateString;
}
The UITextField that triggered the UIDatePicker would be the current firstResponder. You can use this info to identify the textfield that is associated with the currently active UIDatePicker.
Add a tag to UIDatePicker and use the tag to find textField from view.
- (void)textFieldDidBeginEditing:(UITextField* )textField {
if (textField.tag == 603) {
datePicker = [[UIDatePicker alloc]init];
[datePicker setTag:textField.tag];
[datePicker setDatePickerMode:UIDatePickerModeDate];
[datePicker setDate:[NSDate date]];
[datePicker addTarget:self action:#selector(showDate:) forControlEvents:UIControlEventValueChanged];
textField.inputView = datePicker;
}
}
- (void) showDate: (UIDatePicker*) myDatePicker {
NSDate* selected = [myDatePicker date];
NSString* date = [selected description];
UITextField *textField = (UITextField *)[self.view viewWithTag:myDatePicker.tag];
}
Here is a screenshot of what I did till now:
So what I am trying to do is when you select "pick a name" Textfield I need a Picker to show up, with the input #"Jack".
Since iOS 3.2, UITextField supports the inputView property to assign a custom view to be used as a keyboard, which provides a way to display a UIPickerView:
You could use the inputView property of the UITextField, probably combined with the inputAccessoryView property. You assign your pickerView to the inputView property, and, to dismiss the picker, a done button to the inputAccessoryView property.
UIPickerView *myPickerView = [[UIPickerView alloc] init];
//myPickerView configuration here...
myTextField.inputView = myPickerView;
Like that. This will not give you a direct way to dismiss the view since your UIPickerView has no return button, which is why I recommend to use the inputAccessoryView property to display a toolbar with a done button (the bar is just for aesthetics, you might as well just use a UIButton object):
UIToolbar *myToolbar = [[UIToolbar alloc] initWithFrame:
CGRectMake(0,0, 320, 44)]; //should code with variables to support view resizing
UIBarButtonItem *doneButton =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(inputAccessoryViewDidFinish)];
//using default text field delegate method here, here you could call
//myTextField.resignFirstResponder to dismiss the views
[myToolbar setItems:[NSArray arrayWithObject: doneButton] animated:NO];
myTextField.inputAccessoryView = myToolbar;
I use this and find this a lot cleaner than adding a subview and animating the UIPicker
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
responder = textField;
if ([textField isEqual:self.txtBirthday]) {
UIDatePicker *datepicker = [[UIDatePicker alloc] initWithFrame:CGRectZero];
[datepicker setDatePickerMode:UIDatePickerModeDate];
textField.inputView = datepicker;
}
return YES;
}
it will work for you .. i have edited it .and for that you have to set delegate for textfield. and create a UIPIckrView in NIb file.
- (BOOL) textFieldShouldBeginEditing:(UITextView *)textView
{
pickrView.frame = CGRectMake(0, 500, pickrView.frame.size.width, pickrView.frame.size.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.50];
[UIView setAnimationDelegate:self];
pickrView.frame = CGRectMake(0, 200, pickrView.frame.size.width, pickrView.frame.size.height);
[self.view addSubview:pickrView];
[UIView commitAnimations];
return NO;
}
Well, you could rely on the UITextFieldDelegate to handle this kind of functionality.
Inside the
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
is where you would set the text of your current UITextField as well as initializing and showing the UIPickerView.
Important notice:
You might also want to conform to the UIPickerViewDelegate.
HTH
Swift:
internal var textFieldHandlerToolBar: UIToolbar = {
let tb = UIToolbar.init(frame: CGRect.init(origin: .zero, size: CGSize.init(width: UIScreen.main.bounds.width, height: 44.0)))
let doneBarButton = UIBarButtonItem.init(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(actionDonePickerSelection))
tb.setItems([doneBarButton], animated: false)
return tb
}()
internal var pickerView: UIPickerView = {
let pv = UIPickerView.init()
return pv
}()
#objc internal func actionDonePickerSelection() {
textField.resignFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
self.pickerView.delegate = self
self.pickerView.datasource = self
}
Use it like this:
textField.inputAccessoryView = self.textFieldHandlerToolBar
textField.inputView = self.pickerView
What you can do is, create a UIButton with custom type on UITextField. Both having equal sizes. On the touch of button you can show UIPickerView.
http://tmblr.co/ZjkSZteCOUBS
I have the code and everything laid out in my blog to do this exactly. But below, I have the basic concept laid out.
Basically the solution involves an opensource project called ActionSheetPicker on github, and implementing the function textFieldShouldBeginEditing on the UITextFieldDelegate. You can dismiss the keyboard there and provide a UIPickerView instead. The basic code is listed here:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// We are now showing the UIPickerViewer instead
// Close the keypad if it is showing
[self.superview endEditing:YES];
// Function to show the picker view
[self showPickerViewer :array :pickerTitle];
// Return no so that no cursor is shown in the text box
return NO;
}
ViewController.h
#interface ChangeCurrencyVC : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
NSArray *availableCurreniesArray;
}
#property (weak, nonatomic) IBOutlet UITextField *chooseCurrencyTxtFldRef;
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
availableCurreniesArray = #[#"Indian Rupee", #"US Dollar", #"European Union Euro", #"Canadian Dollar", #"Australian Dollar", #"Singapore Dollar", #"British Pound", #"Japanese Yen"];
// Do any additional setup after loading the view.
[self pickerview:self];
}
#pragma mark - picker view Custom Method
-(void)pickerview:(id)sender{
UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
pickerView.showsSelectionIndicator = YES;
pickerView.dataSource = self;
pickerView.delegate = self;
// set change the inputView (default is keyboard) to UIPickerView
self.chooseCurrencyTxtFldRef.inputView = pickerView;
// add a toolbar with Cancel & Done button
UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
toolBar.barStyle = UIBarStyleBlackOpaque;
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneTouched:)];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancelTouched:)];
// the middle button is to make the Done button align to right
[toolBar setItems:[NSArray arrayWithObjects:cancelButton, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], doneButton, nil]];
self.chooseCurrencyTxtFldRef.inputAccessoryView = toolBar;
}
#pragma mark - doneTouched
- (void)cancelTouched:(UIBarButtonItem *)sender{
// hide the picker view
[self.chooseCurrencyTxtFldRef resignFirstResponder];
}
#pragma mark - doneTouched
- (void)doneTouched:(UIBarButtonItem *)sender{
// hide the picker view
[self.chooseCurrencyTxtFldRef resignFirstResponder];
// perform some action
}
#pragma mark - The Picker Challenge
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [availableCurreniesArray count];
}
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow: (NSInteger)row forComponent:(NSInteger)component{
return availableCurreniesArray[row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.chooseCurrencyTxtFldRef.text = availableCurreniesArray[row];
}