Search code examples
iosobjective-cuipickerview

UIPickerView rows disappear during scroll


I have implemented several UIPickerViews in different parts of a project. Most have worked fine. However, the latest one has some very odd behaviour which is difficult to describe. In brief, there are 2 abnormalities:

  1. The first time the UIPickerView is displayed, it is empty, and the rows appear to have virtually zero size. If I click the action button again (to make the picker view appear again), it then displayed normally. (UPDATE: This issue does not occur when I comment out the pickerView:rowHeightForComponent: method).
  2. Then the even weirder part is that scrolling the picker view results in items disappearing as they scroll (and scrolling back the other way does NOT make them reappear).

The implementation is fairly straightforward, and similar to what I've done elsewhere, so I can't figure out what I've done wrong. The data source uses a simple array and logging shows that it is working correctly. Even the initial display of the picker, which shows no values on screen, does correctly log the values as per the NSLog line. In fact it logs exactly the same as for the second invocation in which it does display values correctly (before scrolling makes them disappear).

The "Library" bar button invokes the following action in order to configure a UITextField with the UIPickerView as it's input and then make that text field first responder:

- (IBAction)selectStyleFromLibrary:(id)sender {
    _dummyPickerField = [[UITextField alloc] init];
    [self.view insertSubview:_dummyPickerField atIndex:0];

    StylesController *stylesLibrary = [[StylesController alloc] init];
    [stylesLibrary getStyles];

    UIPickerView *libraryPicker = [[UIPickerView alloc] init];
    libraryPicker.delegate = stylesLibrary;
    libraryPicker.dataSource = stylesLibrary;

    _dummyPickerField.inputView = libraryPicker;
    [_dummyPickerField becomeFirstResponder];
}

All of the picker view's delegate and data source methods implemented are:

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 1;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return styleKeys.count;
}

- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
    return 64;
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    NSLog(@"%@", styleKeys[row]);
    return styleKeys[row];
}

Any ideas why this is not behaving normally?

UPDATE: I've also tried it with a simple existing text field, rather than creating one on the fly. It did not help.


Solution

  • Here's how I solved it (with a little inspiration from @dip comments)...

    My data-source/delegate object is a separate object, and it was not being retained explicitly anywhere, so I have now rearranged things to make it a property, and to assign it in viewDidLoad, so that it sticks around.

    I guess the UIPickerView only has a weak reference to it (or two weak references to it), which of course would not be enough for it to hang around providing data and delegations for long.

    So now I have the following (excluding the actual delegation and data source methods):

    @property (nonatomic, strong) StylesController *stylesLibrary;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Metal Tile Very Light"]];
        self.editor = (StyleEditor *)self.tabBarController;
    
        if ( ! self.editor.namedStyle ) {
            nameField.borderStyle = UITextBorderStyleNone;
            nameField.userInteractionEnabled = NO;
        }
    
        _stylesLibrary = [[StylesController alloc] init];
        [_stylesLibrary getStyles];
    }
    
    - (IBAction)selectStyleFromLibrary:(id)sender {
        _dummyPickerField = [[UITextField alloc] init];
        [self.view insertSubview:_dummyPickerField atIndex:0];
    
        UIPickerView *libraryPicker = [[UIPickerView alloc] init];
        libraryPicker.delegate = _stylesLibrary;
        libraryPicker.dataSource = _stylesLibrary;
    
        _dummyPickerField.inputView = libraryPicker;
        _dummyPickerField.inputAccessoryView = [self accessoryViewWithText:@"Done" target:self action:@selector(libraryPickerDone) forField:_dummyPickerField];
        [_dummyPickerField becomeFirstResponder];
    }