Search code examples
iosobjective-cuipickerviewuitableview

Inline UIPicker Implementation


I'm attempting to implement an inline UIPicker inside a table-view cell, similar to both this and this SO question. I believe I'm close in my implementation, but at the moment, no picker is displayed when I select the appropriate cell. Can anyone point me in the right direction in regards to what I'm doing wrong? Thank you!

Below is where I determine what rows occur in each section:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    switch (indexPath.section) {
        case NotificationsSection:
            return [self tableView:tableView cellForAreaOneRowAtIndexPath:indexPath];
            break;
        case RedZoneSection:
            return [self tableView:tableView cellForAreaTwoRowAtIndexPath:indexPath];
            break;
        case TimeOfDaySection:
            return [self tableView:tableView cellForAreaThreeRowAtIndexPath:indexPath];
            break;
        default:
            return nil;
            break;
    }
}

Below is where I check the number of rows in each section. I suspect my problem may lie here, but I am not completely sure.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    switch (section) {
        case AreaOneSection:
            return AreaOneRows;
            break;
        case AreaTwoSection:
            return TotalAreaTwoRows;
            break;
        case AreaThreeSection:
            return TotalAreaThreeRows;
            break;
        default:
            return 0;
            break;
    }
}

Below is where I return the height for each row:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat rowHeight = self.tableView.rowHeight;
    //    if (indexPath.section == TimeOfDaySection && indexPath.row == HourTimeZoneRow  && self.timePickerIsShowing == NO){
    return rowHeight;
}

Finally, below is where I check if the user selected the index path that I want to insert the UIPicker cell below. If they did, then I call a method to show the the picker.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];

    if (indexPath.section == SectionThree && indexPath.row == RowOne  && self.timePickerIsShowing == NO){

        [tableView beginUpdates];
        [self showTimePicker];
        [tableView endUpdates];
    } else{
        [self hideTimePicker];
        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    }
}

Finally, below is where I show and hide the UIPicker.

- (void)showTimePicker
{
    self.timePickerIsShowing = YES;
    self.timePicker.hidden = NO;
    //build the index path to where the picker should be inserted here
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:HourTimeZoneRow + 1 inSection:TimeOfDaySection];

    static NSString *CellIdentifier = @"TimePickerCell";
    UITableViewCell *cell = (UITableViewCell*)[self.tableView  dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    _timePicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 160)];
    [cell.contentView addSubview:self.timePicker];

    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView reloadData];
    self.timePicker.alpha = 0.0f;
    [UIView animateWithDuration:0.25 animations:^{
        self.timePicker.alpha = 1.0f;
    }];
}

- (void)hideTimePicker {
    self.timePickerIsShowing = NO;
    self.timePicker.hidden = YES;
    [self.tableView reloadData];
    [UIView animateWithDuration:0.25
                     animations:^{
                         self.timePicker.alpha = 0.0f;
                     }
                     completion:^(BOOL finished){
                         self.timePicker.hidden = YES;
                     }];
}

Solution

  • In your showPicker function, you don't seem to do anything? You create a cell, do things with it then it dies when that function ends. The cell is not added anywhere from what I can see?

    You need to add the picker inside cellForRowAtIndexPath, for the index path you know needs a picker.

    What I do requires very little coding. I create a prototype cell in interface builder which contains a picker view. In my case I also add a toolbar above the picker which I can put buttons in to allow Cancel and Done. Add suitable properties to pass in the current values for the picker to show initially. Add a delegate to use to inform the creator (your tableView) of changes in picker values. You can wait for it to finish picking, or use it to update live the cell its editing values for by reloading the cell being edited every time the value changes. I prefer that the picker can pick a value and you can commit it or cancel it.

    When a cell needs edited, I update my data model to insert an editing entry then call [tableView reload]. In my case selecting a cell starts editing, clicking cancel/done ends editing.

    The table view at this point will start asking for cells. This time one of those will be your new picker cell, which will be for editing the cell it is below. When you create it you pass it the data model reference for the data it is to edit.

    So you can achieve all this by simply adding a new prototype cell type and creating it inside cellForRowAtIndexPath when required.

    You have a choice as to how to remove the picker. In my case I have Cancel/Done buttons which just removes the entry from the data model and reloads the table again, resulting in it never being created. You could also make the mode as being click on a cell to add and click again to remove. Again you just update the data model and reload. See how the time picker works on the Calander app for a new appointment.

    You may be thinking this is a lot of reloads. However it only affects what is on screen and I found it to be easier than trying to figure out the affected cells all the time. Its also very smooth. When it is working you can always optimize the code.

    Use constraints in the cell to make sure it has the layout you want.