Search code examples
iphoneheightcellshow-hideexpand

iPhone - Expand UITableView with custom cell and hide/show labels on select


  • I have a UITableView within a UIView
  • The table view's cells are custom, and thus managed by a UITableViewCell class

I need to expand/contract cells upon selection, which I have done using the awesome tutorial here. However, I also need to show/hide UILabels upon selection--like a detail view, as you expand the cell, more labels are shown; contract it back to its original size, and those labels are again hidden.

Basically:
Click
Extend length of cell
Show labels

Click again
Contract cell
Hide labels

Only 1 cell open at a time

This all sounds easy enough, but the method prevalently used across the web (the tutorial I linked to above) automatically deselects its cells upon the first touch, which means my hidden UILabels never get a chance to show up.

If I remove

[tableView deselectRowAtIndexPath:indexPath animated:TRUE];

from didSelectRowAtIndexPath, I can get the hidden label to appear, but it does not disappear of course, as the cell does not get deselected until I select a different cell.

How might I make it so that the cell automatically deselects once it returns to its regular height after a user clicks it for the second time? Also, is there a way to limit the table to only one expanded cell at a time because right now, you can fully expand all cells. I'd like it if expanding Cell2 automatically shrunk Cell1 back to its original height.

Thanks all; don't know what I'd do without Stack Overflow at times.

TableViewController.h

@interface TableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
    IBOutlet UITableView *tableView;
    NSMutableDictionary *selectedIndexes;
}
@end

Relevant code from TableViewController.m

@interface TableViewController (private)

- (BOOL)cellIsSelected:(NSIndexPath *)indexPath;

@end

@implementation TableViewController

#define kCellHeight 50.0

- (void)viewDidLoad {
    [super viewDidLoad];

    selectedIndexes = [[NSMutableDictionary alloc] init];
}


- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
    // Return whether the cell at the specified index path is selected or not
    NSNumber *selectedIndex = [selectedIndexes objectForKey:indexPath];
    return selectedIndex == nil ? FALSE : [selectedIndex boolValue];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    }

    return cell;
}

#pragma mark -
#pragma mark Tableview Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Deselect cell
    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];

    // Toggle 'selected' state
    BOOL isSelected = ![self cellIsSelected:indexPath];

    // Store cell 'selected' state keyed on indexPath
    NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
    [selectedIndexes setObject:selectedIndex forKey:indexPath]; 

    // This is where magic happens...
    [tableView beginUpdates];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // If our cell is selected, return double height
    if([self cellIsSelected:indexPath]) {
        return kCellHeight * 2.0;
    }

    // Cell isn't selected so return single height
    return kCellHeight;
}

@end

Also, the UILabels from the UITableCell class:

    - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    if (selected == YES){
        date.hidden = NO;   
    }   
    else if (selected == NO) {
        date.hidden = YES;
    }
}

Solution

  • And I'm an idiot. The simplest solution is always the best, and this one is one line of code:

     self.contentView.clipsToBounds = YES;
    

    when you setup your cell and lay it out.

    Duh.