Considering a data entry form (scrollable view) that has 8 text field entries and 4 date field entries, Is it possible to present a date-picker at the bottom where normally (and how normally a keyboard would pop up), depending on which text field the user tapped into?
I suppose you can present a date picker modally with code, but it seems presenting a date-picker the same way a keyboard is called and presented would be a built in to the SDK or am I missing something? (set an attribute in the nib editor to tell the view that this text field accepts a date, and to give you a date picker option in addition to the variety of keyboards)
It blows my mind why developers don't have a simple way to "pop-up" a calendar or date picker when a declared date field gets the focus.
Any example apps or tutorials that you know of you can point me towards?
I just researched this same question, and while the web is replete with 3rd party libraries and tricky hacks, there's actually a pretty simple, and probably preferred way of accomplishing what you're after.
First of, it leverages the capabilities offered by the UITextField inputView and input accessoryView properties, which together offer endless customization possibilities. The problem for me became knowing how to structure things to get exactly what I wanted. I suggest reading about them here:
https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/InputViews/InputViews.html
As such, I started by declaring two properties:
#property (nonatomic, strong) UIDatePicker *datePicker;
#property (nonatomic, strong) UIView *datePickerToolbar;
and then created each of them with something like the following:
- (UIView*)datePickerToolbar {
if (!_datePickerToolbar) {
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
toolbar.barStyle = UIBarStyleBlack;
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(datePickerCanceled:)];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(datePickerDone:)];
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[toolbar setItems:[NSArray arrayWithObjects:cancelButton, space, doneButton, nil] animated:NO];
_datePickerToolbar = toolbar;
}
return _datePickerToolbar;
}
- (UIDatePicker*)datePicker {
if (!_datePicker) {
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeDate;
datePicker.date = [NSDate date];
_datePicker = datePicker;
}
return _datePicker;
}
And of course, you can customize these views to suit your needs. Since these views are your own, however, you'll also need to decide how each button in the toolbar should handle interaction. While both should dismiss the "keyboard" (which is done by resigning first responder), in my case I also wanted the Done button to set the date input field to a formatted string of the selected date:
- (void)datePickerCanceled:(id)sender {
// just resign focus and dismiss date picker
[self.dateOfBirthField resignFirstResponder];
}
- (void)datePickerDone:(id)sender {
NSDate *selectedDate = self.datePicker.date;
// format date into string
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
// format overall string
NSString *dateString = [dateFormatter stringFromDate:selectedDate];
self.dateInputField.text = dateString;
[self.dateOfBirthField resignFirstResponder];
}
After that, the only thing left to do to trigger our keyboard replacement is to assign our custom views to those properties UITextField properties I mentioned earlier, which I chose to do in 'ViewDidLoad`:
- (void)viewDidLoad {
[super viewDidLoad];
self.dateInputField.inputView = self.datePicker;
self.dateInputField.inputAccessoryView = self.datePickerToolbar;
}
I hope this is what you were looking for. After seeing everything out there, I'm convinced that this is the simplest, most manageable way to do it. I also like this approach because it can be modified and repurposed easy enough for different input requirements.
A hidden gem, in my opinion.
Related
I have what appears to be a simple problem.
I have a table view controller (part of a 2 tabbed tab bar) which is populated by the user tapping the plus button in the navigation bar and filling in some information. That takes the user to an "Add Entry" view controller.
I have a second table view (second tab of the tab bar) which also has a plus button in the navigation bar which also calls the Add Entry. However, with this table view, I am already populating a textField and the datePicker to be related to the information it came from.
In the prepareForSegue, I'm setting the date and the text field. That works. However, I'm not entirely sure where to place the code in the Add Entry to say "If you're called from tab 1, leave everything blank and if you're called from tab 2, set the date picker".
In the prepareForSegue:
if ([segue.identifier isEqualToString:#"Create New Entry From Event"])
{
AddEntryViewController *addEntryViewController = (addEntryViewController *)segue.destinationViewController;
[addEntryViewController setSelectedEvent:self.occasion.title];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM d, yyyy"];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:sectionTitle];
[addEntryViewController setSelectedDate:dateFromString];
}
The setSelectedDate is:
- (void)setSelectedDate:(NSDate *)selectedDate
{
_selectedDate = selectedDate;
}
If I set the viewWillAppear to:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.occasionTextField.text = self.selectedEvent;
[self.datePicker setDate:self.selectedDate animated:YES];
}
I get a crash when calling the Add Entry from any other screen but this one which of course isn't desirable.
So I need a way to leave all text fields and the date picker as blank when called from anywhere in the app (which works without the self.datePicker line) and to only SET the datePicker when being called from THAT particular table view.
Any thoughts on this would be really great!
in interface define a BOOL like this.
#property (assign, nonatomic) BOOL fromTabOne;
and add
#synthesize fromTabOne;
in viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.fromTabOne) {
self.occasionTextField.text = self.selectedEvent;
[self.datePicker setDate:self.selectedDate animated:YES];
}
}
in prepareForSegue
if ([segue.identifier isEqualToString:#"Create New Entry From Event"])
{
AddEntryViewController *addEntryViewController = (addEntryViewController *)segue.destinationViewController;
[addEntryViewController setSelectedEvent:self.occasion.title];
if (viewOne) { //if you're on first tab
[addEntryViewController setFromTabOne:YES];
} else {
[addEntryViewController setFromTabOne:NO];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM d, yyyy"];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:sectionTitle];
[addEntryViewController setSelectedDate:dateFromString];
}
}
The error you mentioned in comment probably caused by a nil NSDate or wrong locale settings. Make a nil check before setting. I guess the NSDateFormatter couldn't format your string.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 9 years ago.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Improve this question
How can I show a UIDatePicker from UITableViewCell, and have the datepicker have a toolbar at the top that lefts you resign it? Are there any good tutorials on how to do this?
I've recently just had to do the exact same thing.
Insert a UITextField into your UITableViewCell (you may need to create a custom UITableViewCell depending whether you want it to appear on every dynamic cell of your UITableView or on a single static cell).
Create properties for a UIDatePicker, a UIToolbar, and a UITextField, then in IB hook up the UITextField property to your UITextField created in Step 1 (If you're using a custom UITableViewCell class, that's where the UITextField property would need to go):
#property (strong, nonatomic) UIDatePicker * datePicker;
#property (strong, nonatomic) UIToolbar * datePickerToolbar;
#property (strong, nonatomic) IBOutlet UITextField *textField;
...
#synthesize datePicker, datePickerToolbar, textField;
Setup your UIDatePicker, UIToolbar, and UITextField:
- (void)viewDidLoad {
// Initialise UIDatePicker
datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeTime;
[datePicker addTarget:self action:#selector(datePickerValueChanged:) forControlEvents:UIControlEventValueChanged]; // method to respond to changes in the picker value
// Setup UIToolbar for UIDatePicker
datePickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
[datePickerToolbar setBarStyle:UIBarStyleBlackTranslucent];
UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(dismissPicker:)]; // method to dismiss the picker when the "Done" button is pressed
[datePickerToolbar setItems:[[NSArray alloc] initWithObjects: extraSpace, doneButton, nil]];
// Note: If you're using dynamic cells, the below 2 lines need to be in your cellForRowAtIndexPath method instead.
// Set UITextfield's inputView as UIDatePicker
textField.inputView = datePicker;
// Set UITextfield's inputAccessoryView as UIToolbar
textField.inputAccessoryView = datePickerToolbar;
}
Setup dismissPicker method:
-(void)dismissPicker:(id)sender{
[textField resignFirstResponder];
}
Setup datePickerValueChanged method:
- (void)datePickerValueChanged:(id)sender{
NSDate *selectedDate = datePicker.date;
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"HH:mm"];
[textField setText:[df stringFromDate:selectedDate]];
}
Note: In my code, I needed the interface to display the time, hence the date format set here (HH:mm). Because of this, you will also notice that in my UIDatePicker initialization code in viewDidLoad, I've set its mode to UIDatePickerModeTime. For date selections, you may want to set it to UIDatePickerModeDate.
You could put an invisible UITextField in the UITableViewCell and set the UITextFields inputview to a UIDatePicker. Here us the code:
yourdatePicker = [[UIDatePicker alloc]init];
yourtextField.inputView = yourdatePicker;
I am using ActionSheetPicker[1]. It allows you to show any kind of picker from an action-sheet partly overlapping the current scene. Additionally, you can add buttons (like cancel or "today") and a title to the top of the view.
[1]: Original version by Tim Cinel: https://github.com/TimCinel/ActionSheetPicker
Improvements by Hari Karam Singh: https://github.com/Club15CC/ActionSheetPicker
Fixed deprecations in my fork (I think it spread into the others by now): https://github.com/booiiing/ActionSheetPicker
I have a view with an UITextField and need to show an UIDatePicker (it's hidden below the keyboard) when a button is pressed.
I'm lazily instantiating the date picker because this allows me to show the view way more fast.
This is the code associated with the tap on the button:
- (IBAction)hideKeyboard {
if (!self.datePicker) {
// UIDatePicker allocation and initialization
self.datePicker = ...
...
[self.view addSubview:self.datePicker];
}
[self.textField resignFirstResponder];
}
What happens is that this interfere with the keyboard hiding animation. Actually there is no animation at all.
Another clue I have is that this doesn't happen on the simulator, but only on the actual device (an iPhone 4S).
How can I solve this?
EDIT:
Setting the datePicker as the inputView of the textField doesn't solve my problem.
I can recommend you another solution then hiding keyboard and using button for showing the date picker.
If I understood you correctly you want to show a date picker when user taps on the textfield and then maybe you set the date in textfield.
UITextField has a property
#property (readwrite, retain) UIView *inputView
If you set the inputView property to date picker then the textfield will show date picker instead a keyboard (and even animated like keyboard).
Of course the values need to get your own and set to textfield.
Edit reason: Question edited
I do not know why setting the datepicker as the input view did not help. Anyway if you do not want the keyboard shown and show the datepicker by adding a subview you can use the following delegate method of UITextField
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
implement the delegate method. In the method just add pickerview as subview to your view and return NO for not to show the keyboard.
Edit reason: Commend
Is there any reason why you do not use textfield? If not I would use it, because it makes everything easier. If you want to use for some reason the UILabel then you can watch the keyboard notification. When did the keyboard disapeared and then show the datepicker.
For keyboard notifications please refer here, good explained with sample codes, UIKeyboardDidHideNotification should help you
http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
Quite simple :
Just add a date picker object as the input value of your textfield. No need to show/hide the keyboard.
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar] ;
NSDate *currentDate = [NSDate date];
NSDateComponents *comps = [[NSDateComponents alloc] init] ;
[comps setYear:anyInteger];
NSDate *maximumDate = [calendar dateByAddingComponents:comps toDate:currentDate options:0];
[comps setYear:anyInteger;
NSDate *minimumDate = [calendar dateByAddingComponents:comps toDate:currentDate options:0];
[datePicker setMaximumDate:maximumDate];
[datePicker setMinimumDate:minimumDate];
datePicker.datePickerMode = UIDatePickerModeDate;
textField.inputView = datePicker;
The problem I'm coming across is that I'm unsure how to add touchupinside to the below code for the save date button. I'm just trying to launch a UIDatePicker and submit a date into a text field, and then dismiss it.
So I don't know how to link this programmatically created button to the datePicker method. Not only that but, I haven't used UIDatePicker before, so reading this into a textfield I'm a bit fuzzy on. The text field is dateFieldText.
Once the user taps Save Date then the UIDatePicker should be dismissed.
Thanks for the help. This issue is a bit more complicated for me.
- (void)textFieldDidBeginEditing:(UITextField *)sender
{
sender.delegate = self;
if([sender isEqual:dateFieldText])
{
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc]
initWithTitle:#"Save Date"
style:UIBarButtonItemStyleDone
target:self
action:#selector(datePicker)];
self.navigationItem.rightBarButtonItem = doneButton;
}
else{
UIBarButtonItem *submitButton = [[UIBarButtonItem alloc]
initWithTitle:#"Done"
style:UIBarButtonItemStyleDone
target:self
action:#selector(datePicker)];
self.navigationItem.rightBarButtonItem = submitButton;
}
}
-(IBAction)datePicker
{
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeDateAndTime;
[datePicker addTarget:self action:#selector(saveDate) forControlEvents:UIControlEventValueChanged];
[self.dateFieldText setInputView:datePicker];
}
// I haven't made the saveDate method yet...
You have a few things wired up incorrectly. You need to make the date picker the text field's inputView at the point you create the text field, not after the user taps a button that isn't added until the user puts focus on the text field.
You also don't need a Done button and a Save Date button. Either have a single Done button or have a Save and a Cancel button.
You are also going to need to implement the textFieldDidEndEditing delegate method to remove any buttons that you add.
Your saveDate method will need to update the text field's text with the currently selected date (after converting the date to a string with an NSDateFormatter).
The action for your Done/Save/Cancel button(s) (whatever you end up doing) simply needs to call resignFirstResponder on the text field. This will dismiss the date picker. The button actions may also need to save off or revert the date's value as appropriate.
I have a table view controller with (among others) a cell that is to represent a date. I followed this post "How do I make a modal date picker that only covers half the screen?" and I have it working with one big exception - I can't get the picker to disappear!
I tried registering for the event UIControlEventTouchUpOutside, but it seems that this is not generated by the picker (or at least in the mode that I am using it).
How can I recognize that the user has finished selecting a date?
Also, is there a way to disable the user from inputting directly into the UITextField? I want to force them to use the picker. I saw this post "Disable blinking cursor in UITextField?", but is there another way?
Reagards,
--John
Try this code.. here I am putting an datepicker to a uitextfield.. it will have a done button at the top right navigation bar.. so by clicking done I will user can dismiss the datepicker.. the another best method is by putting a toolbar above the datepicker having the done button.. Try this it will work.. when changing the datepicker you can populate the text field.. Hope this helps..
see this stackoverflow link https://stackoverflow.com/a/4824319/763747 this will have the datepicker with done button as toolbar above the keybord..
#pragma mark -
#pragma mark - TextField Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return TRUE;
}
- (void) textFieldDidBeginEditing:(UITextField *)textField {
itsRightBarButton.title = #"Done";
itsRightBarButton.style = UIBarButtonItemStyleDone;
itsRightBarButton.target = self;
itsRightBarButton.action = #selector(doneAction:);
if ([textField isEqual:itsIncidentDateTextField])
{
itsDatePicker = [[[UIDatePicker alloc] init] autorelease];
itsDatePicker.datePickerMode = UIDatePickerModeDate;
[itsDatePicker addTarget:self action:#selector(incidentDateValueChanged:) forControlEvents:UIControlEventValueChanged];
//datePicker.tag = indexPath.row;
textField.inputView = itsDatePicker;
}
}
- (IBAction) incidentDateValueChanged:(id)sender{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, yyyy"];
itsIncidentDateTextField.text = [dateFormatter stringFromDate:[itsDatePicker date]];
[dateFormatter release];
}
I used a different method for disappearing the UIDatePicker.
create a XIB (nib) file and add to it a UIDatePicker then resize the view so it fits only the UIDatePicker, create a property (make it strong and nonatomic) in your ViewController (or whatever class your using, and synthesize of course).
#property (nonatomic, strong) UIView *myDatePickerView;
#synthesize myDatePickerView;
then create a loadDatePickerView method
- (void) loadDatePickerView
{
UINib *nib = [UINib nibWithNibName:kNIBname bundle:[NSBundle mainBundle]];
NSArray *views = [nib instantiateWithOwner:self options:nil];
self.myDatePickerView = [views firstObject];
[myDatePickerView setFrame:CGRectMake(0, 318, 320, 162)];
[self.view addSubview:myDatePickerView];
}
implement the UITextFieldDelegate, and in the textFieldDidBeginEditing method call the loadDatePickerView method,
[self loadDatePickerView];
to make function create a property which is a UIDatePicker instance
#property (nonatomic,strong)
UIDatePicker *myDatePicker;
(synthesize of course)
now create an IBAction like so:
-(IBAction)datePickerValueChanged:(UIDatePicker *)sender
{
myDatePicker = [[myDatePickerView subviews] lastObject];
//now you can do whatever you want with the DatePicker
}
now connect the IBAction to the picker in the XIB file, that way the XIB is now the UIDatePicker instance you created in the VC, if you want it to disappear you can add a UITapGestureRecognizer (in the ViewDidLoad) and the selector will be another IBAction which removes myDatePickerView from its' superView like this:
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dropPicker:)];
[self.view addGestureRecognizer:tap];
[self datePickerValueChanged:myDatePicker];
}
-(IBAction)dropPicker:(id)sender
{
[myDatePickerView removeFromSuperview];
}