I have a UITextField in a UITableView. I am trying to have a UIPickerView or a UIDatePicker as its input view. Both options are appearing and in case of UIPickerView, it is loading correct data. The problem is that it is not scrollable and user cannot interact with it, choose and scroll between the various options.
Userinteraction is set to yes, delegates are correct and everything is in place.
This is the code:
- (BOOL) textFieldShouldBeginEditing:(UITextField *)textField
{
UIDatePicker *datepicker = [[UIDatePicker alloc] initWithFrame:CGRectZero];
[datepicker setDatePickerMode:UIDatePickerModeDate];
textField.inputView = datepicker;
return YES;
}
Why are you set the inputView in this method?
Do this in viewDidLoad. This is a configuration,
doesn't make sense set a particular inputView each time the user focus on that textField:
- (void)viewDidLoad {
[super viewDidLoad];
UIDatePicker *datePicker = [[UIDatePicker alloc]init];
[datePicker setDate:[NSDate date]];
datePicker.datePickerMode = UIDatePickerModeDate;
[datePicker addTarget:self action:#selector(editTextField:) forControlEvents:UIControlEventValueChanged];
[textField setInputView:datePicker];
}
and then:
- (void)editTextField:(id)sender
{
UIDatePicker *picker = (UIDatePicker *)textField.inputView;
[picker setMaximumDate:[NSDate date]];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
NSDate *eventDate = picker.date;
[dateFormat setDateFormat:#"dd/MM/yyyy"];
NSString *dateString = [dateFormat stringFromDate:eventDate];
textField.text = [NSString stringWithFormat:#"%#",dateString];
}
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];
I am trying to show UIPickerView with UIToolBar but getting some error.
Here is my code -
CGRect toolbarTargetFrame = CGRectMake(0, self.view.bounds.size.height-216-44, 320, 44);
CGRect datePickerTargetFrame = CGRectMake(0, self.view.bounds.size.height-216, 320, 216);
UIView *darkView = [[UIView alloc] initWithFrame:self.view.bounds];
darkView.alpha = 0;
darkView.backgroundColor = [UIColor blackColor];
darkView.tag = 9;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissDatePicker:)];
[darkView addGestureRecognizer:tapGesture];
[self.view addSubview:darkView];
UIDatePicker *picker = [[UIDatePicker alloc] init];
picker.autoresizingMask = UIViewAutoresizingFlexibleWidth;
picker.datePickerMode = UIDatePickerModeDate;
[picker addTarget:self action:#selector(dueDateChanged:) forControlEvents:UIControlEventValueChanged];
[picker setFrame:CGRectMake(0,235,320,120)];
picker.backgroundColor = [UIColor blackColor];
[self.view addSubview:picker];
UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height, 320, 44)];
toolBar.tag = 11;
toolBar.barStyle = UIBarStyleBlackTranslucent;
UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] ;
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissDatePicker:)];
[toolBar setItems:#[spacer, doneButton]];
[self.view addSubview:toolBar];
[UIView beginAnimations:#"MoveIn" context:nil];
toolBar.frame = toolbarTargetFrame;
picker.frame = datePickerTargetFrame;
darkView.alpha = 0.5;
[UIView commitAnimations];
Getting error on this line -
picker.frame = datePickerTargetFrame;
This is Error -
*** Assertion failure in -[UIPickerTableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit_Sim/UIKit-2903.2/UITableView.m:7768
2013-10-03 13:43:12.688 Mistoh Beta 1[7228:a0b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource is not set'
libc++abi.dylib: terminating with uncaught exception of type NSException
Please Help me.Thank you in advance.
I had the same problem, it's some crash that appeared starting at iOS7.03.
It can be solved by moving
[self.view addSubview:picker]; at the end of your routine, basically after setting picker's frame. i.e.
picker.frame = datePickerTargetFrame;
[self.view addSubview:picker]; must be added after all pickers manipulations
I am using UIDatePicker as inputView so the accepted answer from Tao-Nhan didn't work for me. I've been struggling with this bug for a long time, but today I finally found an elegant work-around!
After much investigation, I found out that the crash occurs just after didMoveToSuperview is called on the input view. The trick is to use a 'wrapper' view with UIDatePicker as a subview as the inputView, and to remove the picker just as the inputView is being removed from superview, and to re-add it on the next run on runloop after it's moved to a new superview. If that sounds too confusing, just use the code below as your input view and you'll be fine.
TL;DR Using UIDatePicker as inputView? Here is the workaround that I found:
GSDatePickerInputView.h:
#import <UIKit/UIKit.h>
#interface GSDatePickerInputView : UIView
#property (nonatomic, readonly) UIDatePicker *datePicker;
#property (nonatomic) BOOL useWorkaroundToAvoidCrash;
#end
GSDatePickerInputView.m:
#import "GSDatePickerInputView.h"
#interface GSDatePickerInputView ()
#property (nonatomic, strong, readwrite) UIDatePicker *datePicker;
#end
#implementation GSDatePickerInputView
- (instancetype)init {
if (self = [super initWithFrame:CGRectMake(0, 0, 320, 166)]) {
self.translatesAutoresizingMaskIntoConstraints = NO;
self.backgroundColor = [UIColor whiteColor];
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierISO8601];
datePicker.backgroundColor = [UIColor whiteColor];
[self addSubview:datePicker];
self.datePicker = datePicker;
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.datePicker.frame = self.bounds;
}
- (void)willMoveToSuperview:(UIView *)newSuperview {
if (self.useWorkaroundToAvoidCrash == YES) {
if (newSuperview == nil) {
[self.datePicker removeFromSuperview];
}
}
}
- (void)didMoveToSuperview {
if (self.useWorkaroundToAvoidCrash == YES) {
if (self.superview != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[self addSubview:self.datePicker];
self.datePicker.frame = self.bounds;
});
}
}
}
#end
The key pieces are the methods willMoveToSuperview: and didMoveToSuperview. The dispatch_async GCD function is used to put the datePicker back after the crash would've occurred.
You can then use an instance of this inputView like this:
GSDatePickerInputView *dateInputView = [[GSDatePickerInputView alloc] init];
dateInputView.useWorkaroundToAvoidCrash = YES;
[dateInputView.datePicker addTarget:self action:#selector(datePickerChanged:) forControlEvents:UIControlEventValueChanged];
yourView.inputView = dateInputView;
And you can access the datePicker itself later using this code:
((GSDatePickerInputView *)yourView.inputView).datePicker
One last note - the property useWorkaroundToAvoidCrash is there for cases when in one place it was crashing but in another place it wasn't (which happened to me). It's obviously better to avoid such hackery whenever possible, so only set this property to YES in places where it's actually crashing.
UIDatePicker manages a UIPicker internally. So, the message contains UIPicker in it.
Now to the actual error message:
Do you have enough room when you are trying to show the DatePicker?
One of the reason it will throw this error is if could not find enough real estate to display the entire view.
I've had a similar problem where I had to change the maximumDate and minimumDate on UIDatePicker that was a subview of an inputView on one of many UITextFields in my UITableView, after a lot of tracking it appeared that the problem was setting both dates at the same time. The only way I was able to get this working is by removing the UIDatePicker from my custom input view entirely and creating and adding it back in and setting the new min and max date values. This is the ugliest thing I was forced to do in quite some time.
Have the same problem with UIPickerView in a custom UITableViewCell. I have moved all logic that had dealt with placing of the Picker view on the parent view plus settings it's constraints to - (void)layoutSubviews
After updates it looked like the next way:
- (void)layoutSubviews
{
[super layoutSubviews];
[self addSubview:YOUR_PICKER_VIEW]; //'self' in my case was UITableViewCell
[self addConstraints:PICKER_VIEW_CONSTRAINTS];
}
I had similar crash. I put my code in dispatch_after and crash resolved.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (textField == _txtFieldEndDate)
{
_datePicker.maximumDate = [NSDate dateWithDaysFromNow:100 * 365];
[_datePicker setDate:[NSDate date]];
}
else if (textField == _txtFieldStrtDate)
{
_datePicker.maximumDate = [NSDate date];
}
});
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];
}
I have two textFields and I have replaced default tap behavior from keyboard to DatePicker
-(void)updateTextField:(id)sender
{
UIDatePicker *picker = (UIDatePicker*)_editStartDate.inputView;
_editStartDate.text = [NSString stringWithFormat:#"%#",picker.date];
}
- (void)viewDidLoad
{
UIDatePicker *datePicker = [[UIDatePicker alloc]init];
[datePicker setDate:[NSDate date];
[datePicker addTarget:self action:#selector(updateTextField:) forControlEvents:UIControlEventValueChanged];
[_editStartDate setInputView:datePicker];
...
}
This code create datepicker instead of keyboard and update textField with name editStartDate, but I have 2nd textField with name editEndDate and I don't know how to get value in it too.
Do you have any ideas ?
Try this one:
-(void)updateTextField:(id)sender
{
if([_editStartDate isFirstResponder]){
UIDatePicker *picker = (UIDatePicker*)_editStartDate.inputView;
_editStartDate.text = [NSString stringWithFormat:#"%#",picker.date];
}
if([_editEndDate isFirstResponder]){
UIDatePicker *picker = (UIDatePicker*)_editEndDate.inputView;
_editEndDate.text = [NSString stringWithFormat:#"%#",picker.date];
}
}