I'm having a weird issue when presenting a UIDatepicker as a UITextField subclas's inputView.
What's happening is that the picker is being presented but the second column is being cut by something resulting in having month names truncated at the third or fourth character. Here's a screenshot of this problem:
The whole problem is generated at the begining of the app while setting up some styles, specifically UITableView styles. The line of code responsible for this behaviour is this one:
[[UITableView appearance] setBackgroundColor:[self greyPayAppColor]];
greyPayAppColor is just some custom grey color defined elsewhere. In fact thats the exact color showing in the screenshot covering the months names.
Now, I'm really confused since I don't know how a UITableView apperance message can mess up a UIDatePicker. Another thing is that this problem only occurs with a UIDatePicker used as an inputView of a text field, not on a regular one generated by IB.
For reproduction purposes heres my UITexField subclass that I'm using. It generates two views, a UIDatePicker and a UIToolbar setting them as the text field inputView and inputAccessoryView. It also uses delegation to comunicate when the "Done" and "Cancel" buttons were pressed.
The .h file:
#import <UIKit/UIKit.h>
#protocol DatePickerTextFieldDelegate <NSObject>
#optional
- (void)didDismissDatePickerWithDate:(NSDate *)date; // If date == nil => cancel was pressed
#end
#interface DatePickerTextField : UITextField
#property (nonatomic, weak) id<DatePickerTextFieldDelegate> datePickerDelegate;
#property (nonatomic, strong) NSDate *date;
#end
The .m file:
#import "DatePickerTextField.h"
#interface DatePickerTextField ()
#property (strong, nonatomic) UIToolbar *toolBar;
#property (strong, nonatomic) UIDatePicker *datePicker;
#end
#implementation DatePickerTextField
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setup];
}
return self;
}
- (void)awakeFromNib
{
[self setup];
}
- (void)setup
{
self.datePicker = [self setupDatePicker];
self.toolBar = [self setupToolBar];
self.inputView = self.datePicker;
self.inputAccessoryView = self.toolBar;
}
- (UIDatePicker *)setupDatePicker
{
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeDate;
return datePicker;
}
- (void)setDate:(NSDate *)date
{
_date = date;
if (_date) {
self.text = [self localizedDateString:_date];
[self.datePicker setDate:_date animated:NO];
}
}
- (NSString *)localizedDateString:(NSDate *)date
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
NSString *dateString = [dateFormatter stringFromDate:date];
return dateString;
}
- (UIToolbar *)setupToolBar
{
CGFloat width = [self superview].frame.size.width;
UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, width, 44)];
UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancelPressed)];
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(donePressed)];
toolBar.items = #[cancel,extraSpace,done];
return toolBar;
}
- (void)cancelPressed
{
[self.datePickerDelegate didDismissDatePickerWithDate:nil];
}
- (void)donePressed
{
[self.datePickerDelegate didDismissDatePickerWithDate:self.datePicker.date];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
I never understood why this happened but I managed to get around it by overriding the appearance call in that particular VC given that it didn't contain a tableView, like this:
// override appearence
[[UITableView appearanceWhenContainedIn:[self.superview class], nil] setBackgroundColor:[UIColor clearColor]];
I would still like to understand this relation between UITableView and UIDatepicker. If anyone know please leave a comment!
Thanks.
Related
I am new in iOS and I am facing problem regarding to get the value from custom table view cell to view controller. I am using rate view for rating and I am checking if value of rate is less then 3 then it show have to enter text in the text view and I want to get value in view controller
My code is like this
CustomTableviewcell.h
#interface NextTableview : UITableViewCell<RateViewDelegate,UITextViewDelegate>
{
NSString *StatusValue;
UILabel *lbl;
}
#property(nonatomic,strong) IBOutlet UILabel *staticlbl;
#property(nonatomic,strong) IBOutlet UITextView *commenttxtview;
#property(nonatomic,strong) IBOutlet UILabel *Kpiidlbl;
#property (weak, nonatomic) IBOutlet RateView *rateView;
#property (weak, nonatomic) IBOutlet UILabel *statusLabel;
#end
CustomTableviewcell.m
#synthesize rateView,staticlbl,statusLabel,commenttxtview,Kpiidlbl;
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
commenttxtview.layer.borderWidth = 0.70f;
commenttxtview.layer.borderColor = [[UIColor blackColor] CGColor];
commenttxtview.delegate=self;
UIToolbar* doneToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
doneToolbar.barStyle = UIBarStyleBlackTranslucent;
doneToolbar.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc]initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(doneButtonClickedDismissKeyboard)],
nil];
[doneToolbar sizeToFit];
commenttxtview.inputAccessoryView = doneToolbar;
lbl = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 0.0,90.0, 34.0)];
[lbl setText:#"Enter Text"];
[lbl setFont:[UIFont systemFontOfSize:12]];
[lbl setBackgroundColor:[UIColor clearColor]];
[lbl setTextColor:[UIColor lightGrayColor]];
commenttxtview.delegate = self;
[commenttxtview addSubview:lbl];
statusLabel.hidden=YES;
commenttxtview.hidden=YES;
// Do any additional setup after loading the view from its nib.
self.rateView.notSelectedImage = [UIImage imageNamed:#"not_selected_star#2x.png"];
self.rateView.halfSelectedImage = [UIImage imageNamed:#"half_selected_star#2x.png"];
self.rateView.fullSelectedImage = [UIImage imageNamed:#"selected_star#2x.png"];
self.rateView.rating = 0;
self.rateView.editable = YES;
self.rateView.maxRating = 5;
self.rateView.delegate = self;
Kpiidlbl.hidden=YES;
}
-(void)doneButtonClickedDismissKeyboard
{
[commenttxtview resignFirstResponder];
// commenttxtview.hidden=YES;
}
- (void)textViewDidEndEditing:(UITextView *)theTextView
{
if (![commenttxtview hasText]) {
lbl.hidden = NO;
}
}
- (void) textViewDidChange:(UITextView *)textView
{
if(![commenttxtview hasText]) {
lbl.hidden = NO;
}
else{
lbl.hidden = YES;
}
}
- (void)rateView:(RateView *)rateView ratingDidChange:(int)rating {
self.statusLabel.text = [NSString stringWithFormat:#"%d", rating];
NSLog(#"Rating value =%#",self.statusLabel.text);
StatusValue=statusLabel.text;
NSLog(#"Status Value String =%#",StatusValue);
// Hear I am getting value of rating..in StatusValue..
int status=[StatusValue intValue];
if(status<=3)
{
commenttxtview.hidden=NO;
}
else{
commenttxtview.hidden=YES;
}
}
How can I get label and textview value in viewcontroller and I want to set rate view value to rate view after reload table.
Hear in the Image i am getting five star and If I click on less then 3 star it should have to write comment.I am taking rate value in the label.How can I get both label and textvalue in view controller.Please tell me to update question if you want more data.Thanks in Advance!
So write this in your CustomTableViewCell.h
#property(nonatomic,assign)NSInteger selectedRating;
everytime when the rating is updated in the Cell, you have to change the value of this variable.
In the viewController it depends on when you want to get the value of this cell. For example in this method:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CustomTableViewCell *yourCell = (CustomerTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
NSLog(#"%lu",yourCell.selectedRating);
}
Let me know if this was helpful!
Another way without a variable is to read the NSString in the statusLabel and change the string to a NSInteger variable...
If you are using your delegate...
Then implement the delegate RateViewDelegatein the viewController. The delegate is called in the class to which it is assigned...
Try this
You have to add the #property (nonatomic, weak) NSObject<RateViewDelegate>* delegate;
to the header file of your CustomTableViewCell.h in the method
-(UITableViewCell) cellForRowAtIndexPath....
you have to assign the delegate like this
cell.delegate = self;
Now modify your cell delegate method to this
- (void)rateView:(RateView *)rateView ratingDidChange:(int)rating {
self.statusLabel.text = [NSString stringWithFormat:#"%d", rating];
NSLog(#"Rating value =%#",self.statusLabel.text);
StatusValue=statusLabel.text;
NSLog(#"Status Value String =%#",StatusValue);
// Hear I am getting value of rating..in StatusValue..
int status=[StatusValue intValue];
if(status<=3)
{
commenttxtview.hidden=NO;
}
else{
commenttxtview.hidden=YES;
}
if([self.delegate respondsToSelector:#selector(rateView:ratingDidChange:)]){
[self.delegate rateView:rateView ratingDidChange:rating];
}
}
In my testing project, I am planning to have multiple picker views. For these picker views, there will be UIToolbars and custom UIBarButtons. I tried to create a class method so that I will not have to repeat the codes, but it seems like the button is not showing up.
Picker Done Button.h
#import <UIKit/UIKit.h>
#interface UIBarButtonItem (test)
+ (UIBarButtonItem*)barButtonItemWithTint:(UIColor*)color andTitle:(NSString*)itemTitle andTarget:(id)theTarget andSelector:(SEL)selector;
#end
Picker Done Button.m
#import "PickerDoneButton.h"
#implementation UIBarButtonItem (test)
+ (UIBarButtonItem*)barButtonItemWithTint:(UIColor*)color andTitle:(NSString*)itemTitle andTarget:(id)theTarget andSelector:(SEL)selector
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tintColor = color;
button.backgroundColor = [UIColor yellowColor];
[button addTarget:theTarget action:selector forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithCustomView:button];
return doneButton;
}
#end
And to use this class method on my View Controller.m (I am omitting picker view methods which come after viewDidLoad)
#import "ViewController.h"
#import "PickerDoneButton.h"
#interface ViewController ()
#property (strong, nonatomic) NSArray *numberofPeople;
#property (strong, nonatomic) UIPickerView *countPeople;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.countPeople = [[UIPickerView alloc] init];
self.countPeople.dataSource = self;
self.countPeople.delegate = self;
self.numberofPeople = #[#"1",#"2",#"3"];
UIToolbar *countPeopleToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,0,50)];
countPeopleToolbar.barStyle = UIBarStyleBlackOpaque;
// Call the UIBarButtonItem from PickerDoneButton.h
countPeopleToolbar.items = #[[UIBarButtonItem barButtonItemWithTint:[UIColor redColor] andTitle:#"Done" andTarget:self andSelector:#selector(doneClicked)]];
[self pickerView:self.countPeople didSelectRow:0 inComponent:0];
}
Am I using the class method correctly?
Yes you are doing it correct. Only thing you are missing is you are not setting frame of your button to be set inside doneButton. Since button doesn't have any frame set it doesn't show up. Add this after creating button.
button.frame = CGRectMake(0, 0, 30, 30); // change frame as per your requirement
I am trying to replicate Facebook Messenger App, where there is a UITextView attached to the top of the keyboard.
Due to the nature of this app I need my view to be attached, instead of manually scrolling up and down a ScrollView when the keyboard appears.
This can be achieved by using a inputAccessoryView.
I read the docs on it here.
The documentation is very brief and says:
"This property is typically used to attach an accessory view to the system-supplied keyboard that is presented for UITextField and UITextView objects.
The value of this read-only property is nil. If you want to attach custom controls to a system-supplied input view (such as the system keyboard) or to a custom input view (one you provide in the inputView property), redeclare this property as read-write in a UIResponder subclass.
You can then use this property to manage a custom accessory view. When the receiver becomes the first responder, the responder infrastructure attaches the accessory view to the appropriate input view before displaying it."
I have tried declaring a property
#interface CommentViewController ()
#property (nonatomic, readwrite, retain) UIView *inputAccessoryView;
#end
And then setting it:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 20, 320, 100)];
[view setBackgroundColor:[UIColor greenColor]];
self.inputAccessoryView = view;
}
Then I have tried calling both of these:
[self.tableView becomeFirstResponder];
[view becomeFirstResponder];
Nothing happens. What am I doing wrong?
*Note - Extra information: I am using a UITableViewController that I want to have a UIView attached as an inputAccessoryView. Once I get the view working then I will add in a UITextView and more, but this is mainly an example.
Any help is greatly appreciated!
Add input accessory to your textField or textView rather than to pure UIView.
self.mytextField.inputAccessoryView = view;
The inputAccessoryView is a property of the UIResponder class. It allows you to define a custom input accessory view to display when the receiver becomes the first responder. Usually an instance of UIToolBar should be set as the accessory view.
A toolbar sample:
MYInputAccessoryToolbar.h
typedef void (^MYInputAccessoryToolbarDidDoneTap)(id activeItem);
#interface MYInputAccessoryToolbar : UIToolbar
#property (nonatomic, copy) MYInputAccessoryToolbarDidDoneTap didDoneTapBlock;
+ (instancetype)toolbarWithInputItems:(NSArray *)items;
- (instancetype)initWithInputItems:(NSArray *)items;
- (void)addInputItem:(id)item;
- (void)goToNextItem;
- (void)goToPrevItem;
#end
MYInputAccessoryToolbar.m
#interface MYInputAccessoryToolbar ()
#property (strong, nonatomic) UIBarButtonItem *nextButton;
#property (strong, nonatomic) UIBarButtonItem *prevButton;
#property (strong, nonatomic) UIBarButtonItem *doneButton;
#property (nonatomic, copy) NSMutableArray *inputItems;
#property (nonatomic) NSInteger activeItemIndex;
#property (nonatomic) id activeItem;
#end
#implementation MYInputAccessoryToolbar
+ (instancetype)toolbarWithInputItems:(NSArray *)items {
return [[self alloc] initWithInputItems:items];
}
#pragma mark - Initializations
- (instancetype)init {
self = [super init];
if (self) {
_inputItems = [NSMutableArray new];
_prevButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:101 target:self action:#selector(prevButtonTaped)];
_nextButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:102 target:self action:#selector(nextButtonTaped)];
_doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleBordered target:self action:#selector(doneButtonTaped)];
[_doneButton setTitleTextAttributes:#{NSFontAttributeName:[UIFont boldSystemFontOfSize:17]} forState:UIControlStateNormal];
UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixedSpace.width = 20.0f;
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
NSArray<UIBarButtonItem *> *barButtons = #[_prevButton, fixedSpace, _nextButton, flexSpace, _doneButton];
[self sizeToFit];
self.items = barButtons;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(itemDidBeginEditing:)
name:UITextFieldTextDidBeginEditingNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(itemDidBeginEditing:)
name:UITextViewTextDidBeginEditingNotification
object:nil];
}
return self;
}
- (instancetype) initWithInputItems:(NSArray *)items {
self = [self init];
for (id item in items) {
[self addInputItem:item];
}
return self;
}
#pragma mark - Accessors
- (void)addInputItem:(id)item {
if ([item respondsToSelector:#selector(setInputAccessoryView:)]) {
[item setInputAccessoryView:self];
}
[_inputItems addObject:item];
}
#pragma mark - Actions
- (void)itemDidBeginEditing:(NSNotification *)noticifation {
NSInteger itemIndex = [_inputItems indexOfObject:noticifation.object];
if (itemIndex != NSNotFound && _activeItem != noticifation.object) {
_activeItemIndex = itemIndex;
_activeItem = noticifation.object;
[self activeItemChanged];
}
}
- (void)activeItemChanged {
_prevButton.enabled = _activeItemIndex != 0;
_nextButton.enabled = _activeItemIndex != _inputItems.count - 1;
}
- (void)prevButtonTaped {
[self goToPrevItem];
}
- (void)nextButtonTaped {
[self goToNextItem];
}
- (void)goToNextItem {
[_inputItems[_activeItemIndex + 1] becomeFirstResponder];
}
- (void)goToPrevItem {
[_inputItems[_activeItemIndex - 1] becomeFirstResponder];
}
- (void)doneButtonTaped {
if (_didDoneTapBlock) {
_didDoneTapBlock(_activeItem);
}
[_activeItem resignFirstResponder];
}
#pragma mark - Dealloc
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
}
#end
Now assuming that we have a set of text field fields and text views we could use them to initialize an instance of our toolbar.
MYInputAccessoryToolbar *accessoryToolbar = [MYInputAccessoryToolbar toolbarWithInputItems:#[_passwordCurrentField, _passwordNewField, _passwordVerifyField]];
And then each of these fields will have a custom accessory view like this.
Remove self.inputAccessoryView = view; and then add the code below anywhere after -(void)viewDidLoad { ... } where view is your UIView:
-(void) viewDidLoad {
....
}
- (UIView *)inputAccessoryView
{
return self.view;
}
I am posting this answer to show other people my exact code and how easy it actually was, however all the credit goes to MadNik.
In your view controller class where you want a keyboard, in the implementation add the following:
#implementation CommentViewController {
UIView *toolbar;
UITextView *commentTextView;
UIButton *postComment;
}
The toolbar is the actual view that gets docked to your keyboard, and the rest of the objects go on top of the view.
Next it is as simple as initiating the toolbar and setting its frame:
toolbar = [[UIView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height-50, self.view.frame.size.width, 50)];
[toolbar setBackgroundColor:[UIColor whiteColor]];
I make the frame of the toolbar initially sit right at the bottom of the view controller.
Next I just initiate the rest of the objects I want on my toolbar, e.g the UITextField and a UIButton. Just lay them out how you want:
commentTextView = [[UITextView alloc]initWithFrame:CGRectMake(8, 8, self.view.frame.size.width - 16 - 75, 34)];
[commentTextView setBackgroundColor:[UIColor colorWithWhite:0.97 alpha:1]];
commentTextView.layer.cornerRadius = 5;
[commentTextView setFont:[UIFont fontWithName:#"Avenir Next" size:20]];
[commentTextView setTextColor:[UIColor colorWithWhite:0.35 alpha:1]];
postComment = [[UIButton alloc]initWithFrame:CGRectMake(self.view.frame.size.width-75, 0, 75, 50)];
[postComment setTitle:#"Post" forState:UIControlStateNormal];
[postComment.titleLabel setFont:[UIFont fontWithName:#"Avenir Next" size:20]];
[postComment setTitleColor:[UIColor colorWithRed:(255/255.0) green:(40/255.0) blue:(80/255.0) alpha:1.0] forState:UIControlStateNormal];
Next add your objects to your tool bar:
[toolbar addSubview:commentTextView];
[toolbar addSubview:postComment];
Now this is where the magic happens: You simply set your UITextView's inputAccessoryView to whatever view you want to be docked to the keyboard.
In this case it is toolbar, because the toolbar is acting as a dock that holds everything else.
Now all you need to do is add your toolbar to your view controller, and when you tap the UITextView, since its inputAccessoryView it the toolbar, the toolbar will be docked to the keyboard!
Since I am using a UITableViewController, I had to add my toolbar to the window:
[[[UIApplication sharedApplication]delegate].window addSubview:toolbar];
So simple! No extra classes or anything needs to be made!
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];
}
});
I am creating some custom UIBarButtonItems for my bottom toolbar with the following method:
- (void)initialisePageNoProperties
{
pageNoTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 50, 30)];
[pageNoTextField setDelegate:self];
pageNoTextField.text = #"0";
pageNoTextField.textColor = [UIColor blackColor];
pageNoTextField.backgroundColor = [UIColor whiteColor];
pageNoTextField.textAlignment = UITextAlignmentRight;
pageNoTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
pageNoTextField.borderStyle = UITextBorderStyleRoundedRect;
pageNoTextField.autocorrectionType = UITextAutocorrectionTypeNo; // no auto correction support
pageNoTextField.keyboardType = UIKeyboardTypeNumberPad;
pageNoTextField.returnKeyType = UIReturnKeyGo;
[pageNoTextField setClearsOnBeginEditing:YES];
pageNoBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:pageNoTextField];
pageNoBarButtonItem.style = UIBarButtonItemStyleBordered;
noOfPagesTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 50, 30)];
noOfPagesTextField.text = #"0";
noOfPagesTextField.textColor = [UIColor blackColor];
noOfPagesTextField.backgroundColor = [UIColor clearColor];
noOfPagesTextField.textAlignment = UITextAlignmentLeft;
noOfPagesTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
noOfPagesTextField.enabled = NO;
noOfPagesBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:noOfPagesTextField];
}
These buttons then get added to the bottom toolbar with the following method:
- (void)configurePageNoDisplay
{
if(![self.navigationController isToolbarHidden])
{
NSMutableArray *items = [[self.navigationController.toolbar items] mutableCopy];
bool insertIntoArray = ([items count] == 10); // without the page number display the items array will contain 10 items
if (insertIntoArray)
{
[items insertObject:pageNoBarButtonItem atIndex:3];
}
else
{
[items replaceObjectAtIndex:3 withObject:pageNoBarButtonItem];
}
if (insertIntoArray)
{
[items insertObject:noOfPagesBarButtonItem atIndex:4];
}
else
{
[items replaceObjectAtIndex:4 withObject:noOfPagesBarButtonItem];
}
[self.navigationController.toolbar setItems:items];
[self SetPageNoDisplay:[pdfViewCtrl GetCurrentPage]];
}
}
and the values for these buttons get set as follows:
- (void)SetPageNoDisplay:(NSInteger) pageNumber
{
pageNoTextField.text = [NSString stringWithFormat:#"%d", pageNumber];
noOfPagesTextField.text = [NSString stringWithFormat:#"of %d", [[pdfViewCtrl GetDoc] GetPageCount]];
}
The buttons and the fields they contain are declared as follows:
#property (strong, nonatomic) IBOutlet UIBarButtonItem *pageNoBarButtonItem;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *noOfPagesBarButtonItem;
#property (strong, nonatomic) IBOutlet UITextField *pageNoTextField;
#property (strong, nonatomic) IBOutlet UITextField *noOfPagesTextField;
Originally they were not declared as IBOutlets - but I read a suggestion that doing so would help my problem - unfortunately it did not help.
I hide/show the bottom toolbar in response to a double tap from the user:
- (void)handleDoubleTap:(UITapGestureRecognizer *)gestureRecognizer
{
[self.navigationController setNavigationBarHidden:![self.navigationController isNavigationBarHidden] animated:true];
[self.navigationController setToolbarHidden:![self.navigationController isToolbarHidden] animated:true];
[self configurePageNoDisplay];
//[sideBarTab setHidden:![sideBarTab isHidden]];
}
The problem I have is that once the toolbar has been hidden - the buttons do not appear again when it is re-shown. If I rotate the iPad after re-showing the toolbar then the buttons appear again.
I ended up resolving this issue by taking a different approach. I used a standard UIToolbar, rather than the navigation controller toolbar. Using Interface Builder I was able to add a plain UIView to the toolbar and then add the 2 UITextFields inside the UIView. This way I did not have to worry about all the code to add the text fields to the toolbar inside of bar button items - or calling the code to reinstate the text fields on orientation change, hiding and showing of the toolbar etc. It ended up being a much simpler and more robust approach.
Use the setToolbarItems method in your UIViewController to set the toolbar buttons and that should solve it.