I have a custom UIPickerView with three components that are dependent on one another (i.e., the second one shows values depending on what has been selected in the first one and the third is dependent on the first and second). I am getting the values that are shown in the UIPickerView out of an NSDictionary.
Everything works nicely, except, when I spin two of the components at the same time, the app sometimes crashes (no time to reload data). This is what my pickerView:didSelectRow:inComponent looks like:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSLog(#"Selecting row %d component %d", row, component);
// When A is changed, we need to reload B and C
if (component == 0) {
[pickerView reloadComponent:1];
[pickerView selectRow:0 inComponent:1 animated:YES];
// need to reload the C after reloading B
[self pickerView:pickerView didSelectRow:0 inComponent:1];
}
else if (component == 1) {
[pickerView reloadComponent:2];
[pickerView selectRow:0 inComponent:2 animated:YES];
}
[self updateSelection];
}
Is there a way to prevent the user from spinning more than one component of the picker at a time to prevent a crash?
Thanks!
Good holy Jesus you call didSelectRow:component: recursively!
I know that the way you handle parameters prevents it from going more than two levels deep, but still, don't do that. Ever.
There's no way to prevent the user from spinning more than one component at a time. It is the joy of multitouch and iOS to set everything in motion and watch it all settle down into a consistent state. And because the spinner components take time to settle, the user can set them all atwitter using just one finger. You must learn to write your code accordingly.
In this case, you want to delete all calls to didSelectRow:component: from within itself - it is to be called by the system only. You're on the right track with your use of reloadComponent:, it's just that you're not reloading enough of them:
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSLog(#"Selecting row %d component %d", row, component);
[self updateSelection];
// When A is changed, we need to reload B and C
if (component == 0) {
[pickerView reloadComponent:1];
[pickerView reloadComponent:2];
} else if (component == 1) {
[pickerView reloadComponent:2];
}
}
I don't know what your updateSelection does - presumably it sets the data model based on the value of pickerView. This is fine - but it needs to happen at the top of your method, so that when whichever of your titleForRow or viewForRow is called, it has the proper data to choose from. Also, from within any of those three (update,titleFor, viewFor) do not assert values into the picker! Just read from it.
I think this is a good starting point for you, but you will have to tweak things a little here and there as you get closer to your goal. I think you will find that the key to Pickers is that they only assert didSelectRow:component: when one of the component spinner things settles down on a value, and they're really good about continuing to spin if you reload them. Give this a shot and see if you can make some progress, send word back if you get stuck.
Spinning two wheels at a time means you have multi-touch enabled, right?
So, a possibility, may be to just disable multi-touch and limit the user to touching and affecting only one wheel in the UIPickerView.
Here's a link that might help.
Related
The UIPickerView class has a method selectedRowInComponent that returns the selected row for the component. That may or may not be the item that is in the center of the picker view. What I'd like is to be able to tell what index of the pickerView data is currently being displayed (not selected).
To illustrate the difference, if you tap a value in the UIPickerView dial, the row for that value seems to always be returned in selectedRowInComponent. However, if the user scrolls the picker through several values and then lifts the finger, selectedRowInComponent is not updated, or it is updated to the value that just scrolled off.
Is there anyway to determine where the picker component is actually dialed?
The value is always available in method pickerView:didSelectRow:inComponent: but I would like to query it in method pickerView:titleForRow:forComponent:
Why? Because while the UIPickerView is being scrolled selectedRowInComponent is not being called. If for some reason I get to the end of the data and need to add more, I need to do it in a method that is being called. That would seem to be pickerView:titleForRow:forComponent:, which is called for every row.
I know this question is a bit old, but by answer it I could help someone else.
I think I found a solution to this problem you had.
Solution
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
// Passing the row being viewed to a function that does something with it.
[self handleRowBeingViewed:[pickerView selectedRowInComponent:component]];
}
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
// Passing the row being viewed to a function that does something with it.
[self handleRowBeingViewed:[pickerView selectedRowInComponent:component]];
}
/**
* It receives, in input 'rowBeingViewed', the row being viewed by the user in the center of the picker view, and use it to XYZW...
*
*
* #param rowBeingViewed Row currently being viewed by the user in the center of the picker view.
* #return
*/
- (void) handleRowBeingViewed:(NSInteger)rowBeingViewed
{
// Printing for debugging.
NSLog(#"String Being Viewed: %#", pickerViewArray[rowBeingViewed]);
// DO YOUR STUFF HERE
// ...
}
Explanation
The UIPickerView allows you to know which row is selected (or viewed in the center, which I think is the same) at any time by using the method selectedRowInComponent.
If you use that method selectedRowInComponent inside the delegate's method pickerView:titleForRow:forComponent, you can know which row is being viewed in the center every time a new title is being "printed" to the user in the picker view.
Sometimes, if in the Picker View you scroll only 1 or 2 rows, the delegate's method pickerView:titleForRow:forComponent does not get called and you won't know which row is being viewed in the center. To solve that, also use the same method selectedRowInComponent inside the delegate's method pickerView:didSelectRow:inComponent:.
Hope this helps someone.
I have 2 pickerviews on screen. Both populate rotate and taps return
correct and expected results. They are distinguished by the TAG
property using the "if/switch" statement in the necessary delegate
methods.
What I'd like is forpicker1 to change the selected row in picker2
dynamically during runtime as the user interacts.
E.g.
picker1 has values 1 to 10
Picker2 has values red, blue, green, purple etc
User taps row with value 2 in picker1 and automatically picker2
rotates/animates to predetermined row say purple in this case, user then taps picker1 again row 5 and again picker2 is animated/rotated to another predetermined row, and so on
What works:
The two pickers are created in viewDidLoad and
[picker2 selectRow:row inComponent:component animated:NO];
works with no issues, but only first time round at initial view load.
Its does not change the selected row if called from
What does NOT work is calls to:
[picker2 selectRow:row inComponent:component animated:NO]; from other (delegate) methods, even if I call:
[self.thePicker reloadAllComponents];
Does this work at all, or is it not supposed to do this? I'm new to this forum, so apologies in advance if I'm being thick! I have trolled look for the answer though
Cheers
I have multiple textfields on my view, and each time a textfield is taped a picker appears(the picker was made by code). This works fine for the first textfield, I can also select a row and the textfield receives it. But when I tap the second one and the picker appears, the row selected appears on the first textfield. I know I can use "tag" to distinguish them, but then I get stuck...it doesn´t work.
Does anyone have/has this problem? What method do I have to change?
try creating one picker for each textfield pick1,pick2.......then in the
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
method u check
if( pickerview==pick1)
{
text1.text= (pick1 value at row);
}
else if(pickerview==pick2)
{
text2.text= (pick2 value at row);
}
I thought this would be simple, however....
All I want to do is select a row in a picker view on a button press. But this doesn't work.
int row;
row = [self.teamList indexOfObject:lastEvent.TeamWon];
[pickVRPossession selectRow:row inComponent:0 animated:NO];
[pickVRPossession reloadAllComponents]; //I tired this before and after selectRow but makes no difference
I know it's returning the correct index but it always remains on the first (or previously selected item) in the list. Any and all help appreciated.
The [pickVRPossession reloadAllComponents] would cause the UIPicker to reload data from the Delegate and lose your selection from the line of code before.
Lose the last line of your code snippet and you should be good to go.
Im using a uiPickerView that haves images instead of strings for the selections, is working ok for showing the images, I got inspiration from the apple UIcatalog example on how to put the images,
Im implementing it programatically no IB,
So the question is, how to change an image according to the selected cell?
i have
* MainVC (my main view controller)
* CustomView (defining picker size)
* CustomPickerDataSource (the data source for the picker :)
in CustomPickerDataSource, send the data for wich row was selected
- (void)pickerView:(UIPickerView *)pickerView didSelectRow: (NSInteger)row inComponent:(NSInteger)component {
// Handle the selection
NSLog(#"seleccionokkkk: %d", row);
[MainVC entro:row];
}
and in my MainVC
+(void) entro: (int) cuca {
NSLog(#"sumbalo :%d", cuca);
}
I get the number of cell selected on my MainVC, which is where I want to show the different images,
but when selecting the image in my +(void) entro: (int) cuca
I get warnings of course as Im setting instance variables with a class method,
so how can I show the image according to which cell number I receive?
Pseudocode:
if image == 0, show image0
Where to put my image showing code?, and how to set the variable for the incoming message?
thanks a lot!
I'd assume you have as many images as you have items in the picker. Put them into an array in the same order as you did in the picker view. Then take the UIImageView that you want to change to the selected picture, and set it to the picture in the array with the given index.
You can do this in your entro: method (which should be an instance method, no reason that it shouldn't or can't be), or you can just go ahead and do it in the pickerView:didSelectRow: method.
You can set images like this:
UIImageView *myImageView;
NSArray *myImageArray;
...
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:
(NSInteger)row inComponent:(NSInteger)component {
[myImageView setImage:[myImageArray objectAtIndex:row]];
}