views:

245

answers:

2

I have a UIPickerView. I'm customizing it's rows via it's delegate's viewForRow as follows:

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    if (view) {
        return view;
    } else {
        NSString *s = [datePickerValues objectAtIndex:row];

        UILabel *l = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 44)] autorelease];
//        l.text = s;
        l.text = [NSString stringWithFormat:@"%@ r:%ld", s, row];
        l.font = [UIFont boldSystemFontOfSize:18];
        l.textAlignment = UITextAlignmentCenter;
        l.backgroundColor = [UIColor purpleColor];
        return l;
    }
}

When I spin it around for a bit, the rows get mixed up.
I even get the same row two or more times, and sometimes missing rows. The row count is always at 10 tho, it just seems to be calling the delegate's viewForRow method with a wrong row parameter.

I'm using the row paremeter to identify the rows. (as the documentation says). It has a single component, so the component param is always 0, I've verified this with the debugger.

Another weird thing, according to the documentation, once I create the view for a specific row, the delegate's view parameter will have that view, but using the debugger I've seen that the delegate's viewForRow is sometimes called more than once for the same row with a view = nil.

Any idea why this strange behavior? I'm new to cocoa and Obj-C, am I doing something wrong?

EDIT:

From the docs:
view - A view object that was previously used for this row, but is now hidden and cached by the picker view.

So this means that the 2nd time the delegate's viewForRow is called for a specific row, it will have the view returned on call 1. This makes sense since this way the delegate wouldn't have to re-create the view over and over as the user spins the control.
I've verified that in fact the viewForRow is called EVERY time a row is displayed, even if it was previously displayed.

What then is the use for the view parameter? The two answers so far don't seem to be valid.

+1  A: 

Looks like the problem is that UIPickerView tries to reuse your already created views for new rows for performance reasons. In this case ( (view != nil) ) you just return the same view you had for some previous row. If your view is always is a UILabel you can rewrite your code like that (sorry didn't compile it):

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    NSString *s = [datePickerValues objectAtIndex:row];

    UILabel *l = (view != nil)? view : [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 44)] autorelease];
    l.text = [NSString stringWithFormat:@"%@ r:%ld", s, row];
    l.font = [UIFont boldSystemFontOfSize:18];
    l.textAlignment = UITextAlignmentCenter;
    l.backgroundColor = [UIColor purpleColor];
    return l;
}

it must work ok.

Vladimir
yes, it does work in this case, but please read my above edit
Prody
It's just my guess now, but it easy to check. There must be some confusion of terms - the actual row you have in your picker (and data source) and the row the picker displays. So with the 2nd definition you have just 7 rows for standard picker - 5 visible and 2 for scrolling purposes. I've added NSLog messages - UIPicker creates exactly 7 views for me.But I agree that docs appear somewhat confusing in that point.
Vladimir
+1  A: 

You must update the data in the view each time this method is called. By not updating you are returning stale/duplicated data.

zaph
please read my above edit
Prody