Dale Townsend
Dale Townsend

Reputation: 691

Collapse a table row when a button is tapped

I have a table view that has 3 rows in it. When one of the rows is tapped, the row height expands to show a UICollectionView with 3 cells in it.

I want the row to reduce height again when one of the collection view cells is tapped. This is what I've attempted:

//customCell.m

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    ProductViewController *productView = [[ProductViewController alloc]init];
    [productView collapseRow];
}

//ProductViewController.m

@property NSIndexPath *selectedRow;

- (void)collapseRow {
    menuCell *cell = (menuCell *)[self.tableView cellForRowAtIndexPath:self.selectedRow];
    [self.tableView beginUpdates];
    self.selectedRow = nil;
    [self.tableView endUpdates];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView beginUpdates];
    if ([indexPath compare:self.selectedRow] == NSOrderedSame) {
        self.selectedRow = nil;
    } else {
        self.selectedRow = indexPath;
    }
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; {
    if ([indexPath compare:self.selectedRow] == NSOrderedSame)
        return 105;
    else 
        return 50;
}

Am i doing anything wrong here? I know that the table view is updating when I tap a collection view cell, but it's not checking against the currently open row and setting the height using that.

Upvotes: 0

Views: 105

Answers (4)

Sanjay Mohnani
Sanjay Mohnani

Reputation: 5967

The things we have a ViewController(ProductViewController), containing an instance of UITableView, the cells of table view are created in CustomCell class.

1) Since ProductViewController has the instance of UITableView, so using that instance ProductViewController can call methods on UITableView and it's cells.

2) Also, while creating table view we set the delegate and dataSource of tableview as

tableView.delegate = self;
tableView.dataSource = self;

self is nothing but the reference of the class in which table view is being created(in your case it's ProductViewController)

you implement the tableView's delegate and dataSource method in the ProductViewController class

and what the table view does, if it needs to call a method on your class ProductViewController' instance it uses the reference contained in it's delegate and dataSource properties, which is nothing but the reference of ProductViewController. So calling the method using delegate/dataSource by table view will execute the methods in ProductViewController(if the method exists there).

3) Now as you have created a class called CustomCell, using which you create the custom cells and add on the table view, the table can call methods on the cell's instance but if you need the other way round(cell calling the method of table view or instance of ProductViewController, you can't do so since the cell does not know the reference/address of the ProductViewController), so to call a method on the instance of ProductViewController which contains the table view and cell, the cell should know the address of the ProductViewController. And to provide the cell with the reference/ address of ProductViewController we use the concept of delegation as used by UITableView in 2 point point above.

You can use the below delegation pattern implementation to meet the requirement of point 3-

In CustomCell.h class declare a delegate as

@protocol CustomCellDelegate;

@interface CustomCell : UITableViewCell {
    //declare member variables
    ...
    id<CustomCellDelegate> cellDelegate;
}
...
@property (nonatomic, assign) id<CustomCellDelegate> cellDelegate;
...
// method declarations
@end

@protocol CustomCellDelegate <NSObject> 
- (void)collapseRow;
@end

in CustomCell.m

#import "CustomCell.h"

@implementation CustomCell

@synthesize cellDelegate = _ cellDelegate;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

// methods to create cells
// methods to create collection view and it's items

// collection view item selection handler 
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    // using delegate will call the method collapseRow in ProductViewController 
    [self. cellDelegate collapseRow];
}
@end

Also, adopt the delegate in ProductViewController.h as

#import "CustomCell.h"

@interface ProductViewController:UIViewController < CustomCellDelegate >

and in in ProductViewController.m

when you are creating the cell as

CustomCell *cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];

pass the reference of ProductViewController instance as

cell.cellDelegate = self;

Upvotes: 1

Sanjay Mohnani
Sanjay Mohnani

Reputation: 5967

I think the actual problem for you lies in this method

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    ProductViewController *productView = [[ProductViewController alloc]init];
    [productView collapseRow];
}

In the above method you are creating a new instance of ProductViewController using ProductViewController *productView = [[ProductViewController alloc]init]; and this will call the collapseRow, but as it's not on the instance of the previous ProductViewController class so the row of the table contained in the previous instance will not get collapsed.

In above method you should use delegation pattern, when you are creating the cells for the collection view than during cell creation just pass the reference of ProductViewController instance(using delegation) and when any collection view cell is selected, just use the same delegate(which contains the reference of ProductViewController instance) to call the collapseRow method on the correct class rather than allocating the new one.

Upvotes: 1

Sanjay Mohnani
Sanjay Mohnani

Reputation: 5967

You can also use

- (void)collapseRow 
{
    NSArray* indexArray = [NSArray arrayWithObjects:self.selectedRow, nil];
    self.selectedRow = nil;
    [self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationNone];
}

Upvotes: 1

marosoaie
marosoaie

Reputation: 2371

This should work:

- (void)collapseRow {
    NSIndexPath *selectedRow = self.selectedRow;
    [self.tableView beginUpdates];
    self.selectedRow = nil;
    [self.tableView reloadRowsAtIndexPaths:@[selectedRow] withRowAnimation:UITableViewRowAnimationNone];
    [self.tableView endUpdates];
}

Or you could simply:

- (void)collapseRow {
    self.selectedRow = nil;
    [self.tableview reloadData];
}

but that would cause all the cells to be reloaded, so it's not as efficient.

Upvotes: 0

Related Questions