3

I'm trying to get a UIPicker that shows the selected row with green text. I use the pickerView:viewForRow:forComponent:reusingView: method to create a UILabel for it:

- (UIView*) pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    UILabel *label = (UILabel*)view;
    if (label == nil) {
        label = [UILabel new];
        label.font = [UIFont boldSystemFontOfSize: 24];
        label.adjustsFontSizeToFitWidth = YES;
    }
    UIColor *color = (row == [pickerView selectedRowInComponent: component]) ? RGBx(0x579B2F) : UIColor.whiteColor;
    label.textAlignment = component == 0 ? NSTextAlignmentRight : NSTextAlignmentLeft;
    NSMutableAttributedString *string;
    if (component == 0) {
        string = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%3lu.", row]];
        [string addAttribute:NSForegroundColorAttributeName value: color range:NSMakeRange(0, string.length - 1)];
        [string addAttribute:NSForegroundColorAttributeName value: [UIColor clearColor] range:NSMakeRange(string.length - 1,1)];
    }
    else {
        string = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@".%02lu", row % 60]];
        [string addAttribute:NSForegroundColorAttributeName value: [UIColor clearColor] range:NSMakeRange(0,1)];
        [string addAttribute:NSForegroundColorAttributeName value: color range:NSMakeRange(1,string.length - 1)];
    }
    label.attributedText = string;
    return label;
}

There's some additional things in there, to get the two wheels aligned closely, but with a little bit of spacing (the transparent periods). It mostly works:

enter image description here

Initially, it looks perfect, green selections and white/gray others. The problem is that when I scroll the wheels, they don't always end up green. Sometimes they do, but not always. As can be seen, sometimes the scrolled value will stay green even if it's not selected anymore (see the 09 in the top right corner).

What do I do to keep the green only and always on the selected row?

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
  • 1
    You have a debugger. Debug! My guess is that calling `selectedRowInComponent` inside `viewForRow` doesn't always work. Add some logging and see whether that's coming out correctly the whole time. – matt Aug 05 '15 at 17:15
  • Some `NSLog`ing shows that yes it's kind of wishy washy about when it invokes the the `pickerView:viewForRow:forComponent:reusingView:` method. Sometimes not at all (it's already got something), and often before the selection is updated. – Travis Griggs Aug 05 '15 at 18:16
  • Careful use of `reloadComponent`, causes these caches to be flushed apparently, forcing it to call said method again and get the "up to date" correct presentation. Someone should put that in an `Answer` for points... – Travis Griggs Aug 05 '15 at 18:18
  • Did you know that you are allowed to answer your own question? (I do think, however, that @Idali has already put a finger on the matter, so you could just accept that answer. Or you could do both.) – matt Aug 05 '15 at 18:21
  • Yep, I answer my own questions plenty. Just trying to share the wealth a little. We don't all have 100K points. :) – Travis Griggs Aug 05 '15 at 18:45
  • Well, those of us who do would prefer _you_ to answer the question so we can upvote your answer. :)))) – matt Aug 05 '15 at 18:48

2 Answers2

1

If a row was green initially, if you scroll up or down as far as this row is still visible it will keep the green color since viewForRow:(NSInteger)row forComponent: will not be called on that row (it will be called if this row was previously not visible and need to be visible)...

while you need in your selectedRowinCompoment to set green to selected you need also to remove green color from old selected row (in case it's still visible).

Idali
  • 1,023
  • 7
  • 10
0

@idali diagnoses the problem well. And hints at what needs to happen. But didn't give the actual answer.

The answer is to use reloadComponent: (or reloadAllComponents). It should be done any time the user changes the selection, or you programmatically change the value. E.g.

-(void) pickerView:(UIPickerView*) pickerView didSelectRow:(NSInteger) row inComponent:(NSInteger) component {
    [pickerView reloadComponent: component];
}

and any time you use selectRow:inComponent:animated::

...
[self.picker selectRow: hours inComponent: 0 animated: YES];
[self.picker reloadAllComponents];
...
Travis Griggs
  • 21,522
  • 19
  • 91
  • 167