iOS Developer
iOS Developer

Reputation: 1723

Expanding and Collapsing table view cells in ios

I have a table view of custom cells and some buttons in each cell.Clicking on any of the button inside the cell will reveal another custom view below that cell.Next click on the same button will collapse the view and need this same for all cells.I tried with insertrow method on the button click but in vain.How can i do this with using only the table view delegates.

This is what i tried:

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

    CustomCellFor_Dashboard  *customCell = (CustomCellFor_Dashboard *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (customCell == nil)
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCellFor_Dashboard" owner:self options:nil];
        customCell = [nib objectAtIndex:0];
    }
    [customCell.howyoulfeelBtn  addTarget:self action:@selector(buttonclicked:) forControlEvents:UIControlEventTouchUpInside];
      customCell.nameLabel.text = @"test";
    customCell.imgView.image = [UIImage imageNamed:@"Default.png"];
   // customCell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
    return customCell;
}

-(void)buttonclicked:(id)sender{
    NSIndexPath *indexPath = [myTable indexPathForCell:sender];


    [myTable beginUpdates];

     NSIndexPath *insertPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
   [myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:insertPath] withRowAnimation:UITableViewRowAnimationTop];
  } 

can anyone help me?

Upvotes: 20

Views: 52938

Answers (4)

eagle.dan.1349
eagle.dan.1349

Reputation: 631

I got the same task on one project with just one thing different: There were no buttons, just tapping on cell will expand or collapse it.

There are several things you should edit in your code. First, the button method code will look something like this:

- (void) collapseExpandButtonTap:(id) sender
{
    UIButton* aButton = (UIButton*)sender; //It's actually a button
    NSIndexPath* aPath = [self getIndexPathForCellWithButtonByMagic:aButton];
    //expandedCells is a mutable set declared in your interface section or private class extensiont
    if ([expandedCells containsObject:aPath])
    {
        [expandedCells removeObject:aPath];
    }
    else
    {
        [expandedCells addObject:aPath];
    }
    [myTableView beginEditing];
    [myTableView endEditing]; //Yeah, that old trick to animate cell expand/collapse
}

Now the second thing is UITableViewDelegate method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([expandedCells containsObject:indexPath])
    {
        return kExpandedCellHeight; //It's not necessary a constant, though
    }
    else
    {
        return kNormalCellHeigh; //Again not necessary a constant
    }
}

Key thing here is to determine if your cell should be expanded/collapsed and return right height in delegate method.

Upvotes: 33

ajw
ajw

Reputation: 2702

I also had a same situation and my solution was to put a button on top of the Section Title with viewForHeaderInSection method.

noOfRows defines how many rows are there in each section and button.tag keeps which button of section is pressed.

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIButton *btnSection = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height)];
    btnSection.tag = section;   
    [btnSection setTitle:[sectionArray objectAtIndex:section] forState:UIControlStateNormal];
    [btnSection addTarget:self action:@selector(sectionButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

    return btnSection;
} 

- (void)sectionButtonTapped:(UIButton *)button {
    sectionIndex = button.tag;
    if (button.tag == 0) {
        noOfRows = 3;
    } else if (button.tag == 1) {
        noOfRows = 1;
    } else if (button.tag == 2) {
        noOfRows = 2;
    }

    [self.tableView reloadData];
}

Hope this will help you..

Upvotes: 0

kgaidis
kgaidis

Reputation: 15579

Saw this post and just wanted to give my 2 cents as my solution to this is very similar to the chosen answer (the tapping of a whole area).

Many people architect this by using just cells alone, but I believe there is a way to build this that might align better with what people are trying to achieve:

There are headers and there are cells. Headers should be tappable, and then cells underneath the headers would show or hide. This can be achieved by adding a gesture recognizer to the header, and when tapped, you just remove all of the cells underneath that header (the section), and viceversa (add cells). Of course, you have to maintain state of which headers are "open" and which headers are "closed."

This is nice for a couple of reasons:

  1. The job of headers and cells are separated which makes code cleaner.
  2. This method flows nicely with how table views are built (headers and cells) and, therefore, there isn't much magic - the code is simply removing or adding cells, and should be compatible with later versions of iOS.

I made a very simple library to achieve this. As long as your table view is set up with UITableView section headers and cells, all you have to do is subclass the tableview and the header.

Link: https://github.com/fuzz-productions/FZAccordionTableView

enter image description here

Upvotes: 10

Kalel Wade
Kalel Wade

Reputation: 7782

Going off of what @eagle.dan.1349 said, this is how to do it on the clicking of the cell. In storyboard, you also need to set the table cell to clip subviews, otherwise the content that would be hidden will show.

.h

@property (strong, nonatomic) NSMutableArray *expandedCells;

.m

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    if ([self.expandedCells containsObject:indexPath])
    {
        [self.expandedCells removeObject:indexPath];
    }
    else
    {
        [self.expandedCells addObject:indexPath];
    }
    [tableView beginUpdates];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat kExpandedCellHeight = 150;
    CGFloat kNormalCellHeigh = 50;

    if ([self.expandedCells containsObject:indexPath])
    {
        return kExpandedCellHeight; //It's not necessary a constant, though
    }
    else
    {
        return kNormalCellHeigh; //Again not necessary a constant
    }
}

Upvotes: 17

Related Questions