UIPickerView - In iOS5 didSelectRow is not called for all rows - ios5

Note: I have the delegate set properly and UIPicker works perfectly in ios6.
In ios5 , the call back for didSelectRow: is called only if i select row0 or row5 only . I have 12 rows .
Does anyone have clue what could be wrong?
NOte: The UIPicker's delegate and data source are pointing to the class having the below code.
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
if ([pickerView isKindOfClass:[MIDatePicker class]]) {
switch (self.pickermode) {
case MIDatePickerModeDayMonth:
return 2;
break;
case MIDatePickerModeDayMonthYear:
return 3;
break;
default:
break;
}
}
return 3;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
if ([pickerView isKindOfClass:[MIDatePicker class]]) {
//month , day , year
switch (component) {
case 0:{
return [self.monthsArray count];
}
break;
case 1:
{
return [self.daysArray count];
}
case 2:{
return [self.yearArray count];
}
default:
break;
}
}
return 3;
}
// returns width of column and height of row for each component.
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{
// month , day ,year
switch (component) {
case 0:{
return self.picker.frame.size.width/2;
}
case 1:
return self.picker.frame.size.width/6;
case 2:{
return self.picker.frame.size.width/3;
}
default:
break;
}
return self.picker.frame.size.width;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
return pickerView.frame.size.height/5;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
// month , day ,year
switch (component) {
case 0:{
return [NSString stringWithFormat:#" %# ",[self.monthsArray objectAtIndex:row]];
}
case 1:
return [self.daysArray objectAtIndex:row];
case 2:{
return [self.yearArray objectAtIndex:row];
}
default:
break;
}
return #"";
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
NSLog(#"selected row:%d , component:%d" , row,component);
}

In ios5: Now i am getting all the calls for didSelectRow by casting float to integer.
My observations: The default picker height is 216.0 . The same piece of code works if i change the row height to pickerView.frame.size.height/3 or pickerView.frame.size.height/4 or even pickerView.frame.size.height/6; reason: 216.0 is perfectly divisible by 3,4,6 and when divided by 5 it has 0.2 fraction. Thats the reason i am getting the didSelect call only for row1 and row6 in case of pickerView.frame.size.height/5.
Decent Fix might be:
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
return (int) pickerView.frame.size.height/5;
}

Related

How to make UIPickerView with 3 visible rows?

How to make uipickerview with only 3 visible rows? Like this:
You have to add UIDatePicker Programmatically to display three rows. Adjust the picker size according to your need.
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.frame = CGRectMake(0, 160, 320, 160);
datePicker.datePickerMode=UIDatePickerModeDate;
[self.view addSubview:datePicker];
I noticed that most of the responses mentioned either UIDatePicker or the number of Components, neither of which seem right since the question specifically asks about UIPickerView and the number of Rows Visible.
I found a solution by implementing:
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return (80.0);
}
This allows you to make the height of the row larger (or smaller) so that fewer (or more) rows appear in the control, although the vertical size of the control is the same.
I do believe that the size of the control can be changed to avoid all the excess spacing, but I haven't addressed that issue yet.
Row Height 25.0
Row Height 80.0
In addition to #GRW's answer. So, you cant set arbitrary height to a picker view; but you Can: change row height to have 3 visible rows; AND apply affine transform to scale down the whole picker view.
To maintain the aspect ratio:
you must scale not only y, but x too with the same scale factor;
divide -pickerView:widthForComponent: by the scale factor;
divide -pickerView:rowHeightForComponent: by the scale factor;
if you have labels / text in components' rows, the font size must by divided by the factor as well.
// consider
CGFloat _scaleFactor = 0.5;
- (void)viewDidLoad
{
[super viewDidLoad];
self.pickerView.transform = CGAffineTransformMakeScale(_scaleFactor, _scaleFactor);
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return desiredHeight / _scaleFactor;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component;
{
return desiredWidth / _scaleFactor;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return desiredHeight / _scaleFactor;
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row
forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *label = (UILabel *)view;
if (!label) {
label = [[UILabel alloc] init];
CGFloat desiredFontSize;
label.font = [UIFont systemFontOfSize:(desiredFontSize / _scaleFactor)];
}
// set text from data source
return label;
}
The difference versus blocking out not-to-be-visible rows is quite subtle; however, in my opinion the scaled picker on the right looks way more natural... In fact, that makes a big difference.
(scale factor used is 0.5)
Try following code -
#pragma mark - picker datasource methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return 3;
}
#pragma mark - picker delegate
-(NSString *) pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [YourArray objectAtIndex:row];//your array that contains data
}
Please try below code :
//Method to define how many columns/dials to show
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 3;
}
// Method to define the numberOfRows in a component using the array.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent :(NSInteger)component
{
if (component==0)
{
return [arrFirst count];
}
else if (component==1)
{
return [arrSecond count];
}
else
{
return [arrThird count];
}
}
// Method to show the title of row for a component.
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
switch (component)
{
case 0:
return [arrFirst objectAtIndex:row];
break;
case 1:
return [arrSecond objectAtIndex:row];
break;
case 2:
return [arrThird objectAtIndex:row];
break;
}
return nil;
}
Thanks, Do let me know if you have any problems.

Issue with UIPickerView in didSelectRow

Hi have 4 component in my UIPickerView
When i am calling didSelectRow in UIPickerView.
When I am Rolling selectedRowInComponent:1
I am displaying NSLog(#"hours");
When I am Rolling selectedRowInComponent:0
I am displaying NSLog(#"date");
when i am scrolling selectedRowInComponent:1
It still display me date ... Not hours.
#any one help me out
Thanks in advance.
- (void)pickerView:(UIPickerView *)thePickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
//get the values
NSInteger date_NewRow = [thePickerView selectedRowInComponent:0];
NSInteger hour_NewRow = [thePickerView selectedRowInComponent:1];
NSInteger minute_NewRow = [thePickerView selectedRowInComponent:2];
NSInteger am_pm_NewRow = [thePickerView selectedRowInComponent:3];
if (date_NewRow) {
NSLog(#"date");
}
else if (hour_NewRow) {
NSLog(#"hours");
}
//some code to save the values
}
IMHO you might be checking the wrong values. If you want to know which component changed, check the component variable:
if (component==0) NSLog(#"date");
else if (component==1) NSLog(#"hour");
else if (component==2) NSLog(#"minute");
else if (component==3) NSLog(#"am-pm");
Best,
Sascha

How do you change the size of the components in a uipicker

I have a picker returning 3 components. I was told you can change the size of the views by doing the following, but I am not too sure how to implement this...
implementing the UIPickerViewDelegate method pickerView:widthForComponent: method to size the components (probably 150, 20, and 150, or so).
I see in the documentation you set up a method like this, but dont know what to do next?
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
}
For using this you have to implement UIPickerViewDelegate...... and assign it to your pickerView.....
if this method is being written in the same class where you are creating pickerView......
then write.....
pickerView.delegate = self;
and in header implement this delegate...
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
if(component == 0)
return 150.0;
else if(component == 1)
return 20.0;
else if(component == 2)
return 150.0;
else
return 150.0;
}
Components are numbered left-to-right.
Thanks,

Dependent UIPickerView

Does any one know how to make dependent UIPickerView. For example when i select row 2 of component one the titles for component two change?
I have looked on the internet there is no real answer, i have tried using if and switch statements but they just crash.
It depends on how you are going to keep data. For an example if you have an array as the value of a key of a dictionary, and that dictionary have different such keys, the first column will be the keys, and on selecting one you will be displaying the array in the other column (component). - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView method should return 2.
In
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
method, you need to give the number of keys in dictionary for component 1, and count of the array of the currently selected key.
eg
if(component==0) return [[DICTIONARY allKeys] count];
else return [[DICTIONARY objectForKey:#"SELECTED_KEY"] count];
Then,
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
selectedIndex = [pickerView selectedRowInComponent:0];
if (component == 1 && !(selectedIndex < 0)) {
[pickerView reloadComponent:2];
[pickerView selectRow:0 inComponent:2 animated:YES];
}
}
and
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UILabel *pickerRow = (view != nil) ? view : [[[UILabel alloc] initWithFrame:CGRectMake(5, 0, 115, 60)] autorelease];
pickerRow.font = [UIFont boldSystemFontOfSize:14];
pickerRow.textAlignment = UITextAlignmentLeft;
pickerRow.backgroundColor = [UIColor clearColor];
pickerRow.textColor = [UIColor blackColor];
pickerRow.numberOfLines = 0;
if (component == 0) {
pickerRow.text = #"DICTIONARY_ROW'th_KEY";
}
else {
pickerRow.text = [[dictionary objectForKey:#"SELECTED_KEY"] objectAtIndex:row];
}
return pickerRow;
}
I'm sure this can be accomplished using a combination of the delegate method pickerView:didSelectRow:inComponent: and reloadComponents.
Hope this helps, I had a similar situation, my requirement was when i select a country name in a component, the cities of the country should be loaded in the second component. So what i did was, i had 2 pickerview side by side and when the user selects a row in country picker i re-initiated the second picker with appropriate data.
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (pickerView == countryPickerView)
{
if(PickerViewCity)
{
[PickerViewCity removeFromSuperview];
[PickerViewCity release];
PickerViewCity=nil;
}
PickerViewCity = [[UIPickerView alloc] initWithFrame:CGRectMake(160, 38, 320, 240)];
PickerViewArrayCity=[[countryPickerViewArray objectAtIndex:[pickerView selectedRowInComponent:0]]objectForKey:#"nodeChildArray"];
//NSLog(#"%#",PickerViewArrayCity);
PickerViewCity.showsSelectionIndicator = YES; // note this is default to NO
PickerViewCity.delegate = self;
PickerViewCity.dataSource = self;
CGAffineTransform s20 = CGAffineTransformMakeScale(0.5, 0.58);
CGAffineTransform t21 = CGAffineTransformMakeTranslation(-80,-50);
PickerViewCity.transform = CGAffineTransformConcat(s20, t21);
PickerViewCity.layer.masksToBounds = YES;
[someview addSubview:PickerViewCity];
}
if (pickerView == PickerViewCity)
{
}
}
you need to set conditions for each row of the first component (with index 0) and open a switch to select each index, I did it and worked for me:
on ...titleForRow:(NSinteger)row forComponent(NSInteger)component
{
if (component == 0) // this is first component from left to right
return [arrayForFirstComponent objectAtIndex:row];
if (component == 1) // this is second component from left to right
switch([pickerIBOutletAsYouNamedIt selectedRowInComponent:0]) // here you set content of component two depending on which row is selected from component 1
{
case 0: // remember index starts at 0
return [arrayForComponent2ForRow1inComponent1 objectAtIndex:row];
break;
//... and so on for each row in the component 1 (with index 0)
}
}
remember: YOU HAVE TO DO THIS ALSO WITH
- ...numberOfRowsInComponent:(NSInteger)component
and with
- ...didSelectRow:(NSInteger)row inComponent:(NSInteger)component
and in his last one, in selection of row at component 0 call [pikcerIBOutlet reloadComponent:1]
I think, you like to change the corresponding item of one component when change the other. It's maybe a better solution.
Code:
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component {
if (component == 0) {
//Here First Part of the Pickerview is represented using 0.
// dependentPicker is the Pickerview outlet
[dependentPicker selectRow:row inComponent:1 animated:YES];
[dependentPicker reloadComponent:1];
}
else
{
[dependentPicker selectRow:row inComponent:0 animated:YES];
[dependentPicker reloadComponent:1];
}
}
Note: When you use this code the component values must be Hold by two NSArray and the order of the corresponding dependent values must be same in both arrays.

disabling the values in one component of picker based on value selected in other component

I am trying to implement a custom picker. It has 3 components. Now, I want to gray out some values in the second component based on the first component value selected. I have referred to many sites and tried googling out to disable the values.(To be precise, something like the uidatepicker where if we select feb, 29 and 30 will be grayed. But i am trying to do it in custom picker implementing my own contents). Could some one help me how to go about for disabling the values in custom picker? I tried using the function
[myPickerView selectRow:27 inComponent:1 animated:NO];
based on the if conditions also. It directly goes to the value, but doesn't gray out the unnecessary values.
My code:
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSString *returnStr = #"";
// note: custom picker doesn't care about titles, it uses custom views
if (pickerView == myPickerView)
{
if (component == 0)
{
returnStr = [pickerViewArray objectAtIndex:row];
}
else if(component ==1)
{
returnStr = [pickerViewArray1 objectAtIndex:row];
}
else
{
returnStr = [pickerViewArray2 objectAtIndex:row];
}
}
return returnStr;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
CGFloat componentWidth = 0.0;
if (component == 0)
{
componentWidth = 140.0;
}// first column size is wider to hold names
else if(component ==1)
{
componentWidth = 40.0;
}// second column is narrower to show numbers
else if(component == 2)
{
componentWidth = 100;
}
return componentWidth;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return 40.0;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
//return [pickerViewArray count];
if (component == 0)
{
return [pickerViewArray count];
}// first column size is wider to hold names
else if(component ==1)
{
return [pickerViewArray1 count];
}
else
{
return [pickerViewArray2 count];
}
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 3;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (pickerView == myPickerView) // don't show selection for the custom picker
{
// report the selection to the UI label
label.text = [NSString stringWithFormat:#"%# %# %#",
[pickerViewArray objectAtIndex:[pickerView selectedRowInComponent:0]],
[pickerViewArray1 objectAtIndex:[pickerView selectedRowInComponent:1]],[pickerViewArray2 objectAtIndex:[pickerView selectedRowInComponent:2]]];
}
}
I haven't tried it, but there is -pickerView:viewForRow:forComponent:reusingView:. Its documentation says:
If the previously used view (the view parameter) is adequate, return that. If you return a different view, the previously used view is released. The picker view centers the returned view in the rectangle for row.
so if I undertstand corectly, you could take that passed in view, change it (grey out) and return it.
Have you declared that your controller conforms to the protocol in your header file? Do you know if the delegate methods above are being called when you change you selection, particularly the didSelectRow one?
To find out just set a breakpoint on that method, build and run and try changing the pickerview selection.
Once you have that sussed, it is matter of analysing the selection and pulling the correct data source, then reloading the component, which you seem to be missing from the implementation above:
[pickerView reloadComponent:(int)];
I hope it helps.
Cheers,
Rog