0

Imgur

I want to expand the cell when selected to show a row of buttons like the image above. I have a xib file which shows a cell that is 320 wide x 140 tall. I have subclassed that UITableViewCell. Additionally I have another xib file that has the row of buttons as shown in blue ink in the image above.

I am able to load the row of buttons using initWithCoder using this answer from a really smart guy!

However, it overwrites all my other views inside the MyCustomCell.

How can I load the xib just when the cell expands so that it is positioned in the lower half of the 140pt tall cell?

Community
  • 1
  • 1
thewheelz
  • 379
  • 2
  • 15

2 Answers2

0

I'm not sure what you have in your two xib files, but the way I would do it is to have one xib file for the shorter cell, and one for the taller cell (that has everything in it that you show in your drawing). I did it like this, with two xib files like I mention above:

#import "TableController.h"
#import "ShortCell.h"
#import "TallCell.h"

@interface TableController ()
@property (strong,nonatomic) NSArray *theData;
@property (strong,nonatomic) NSIndexPath *selectedPath;
@end

@implementation TableController 


- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"ShortCell" bundle:nil] forCellReuseIdentifier:@"ShortCell"];
    [self.tableView registerNib:[UINib nibWithNibName:@"TallCell" bundle:nil] forCellReuseIdentifier:@"TallCell"];
    self.theData = @[@"One",@"Two",@"Three",@"Four",@"Five",@"Six",@"Seven",@"Eight",@"Nine"];
    [self.tableView reloadData];
}



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.theData.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath isEqual:self.selectedPath]) {
        return 88;
    }else{
        return 44;
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if (! [self.selectedPath isEqual:indexPath]) {
        NSLog(@"returning short");
        ShortCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ShortCell" forIndexPath:indexPath];
        cell.label.text = self.theData[indexPath.row];
        return cell;
    }else{
        NSLog(@"returning long");
        TallCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TallCell" forIndexPath:indexPath];
        cell.label.text = self.theData[indexPath.row];
        return cell;
    }

}


#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    if ([self.selectedPath isEqual:indexPath]) {
        self.selectedPath = nil;
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        return;
    }

    NSIndexPath *lastSelectedPath = self.selectedPath;
    self.selectedPath = indexPath;
    if (lastSelectedPath != nil) {
         [self.tableView reloadRowsAtIndexPaths:@[indexPath,lastSelectedPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }else{
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • I tried this and it works. However, when I click again on the selectedCell, it crashes the app. Any ideas? Also, I noticed it doesn't exactly perform the desired behavior of expanding the cell like an accordion to reveal the button row. It kinda fades in the new TallCell in place of the ShortCell. Any idea how to create that effect? – thewheelz Apr 14 '13 at 16:37
  • @AlanMond, Sorry, I forgot to take into account picking the same row that was already expanded. I edited my answer to fix that. I don't know a way to show an expansion of the bottom. – rdelmar Apr 14 '13 at 21:57
  • yes, that fixed it! Can you explain the [self.tableview reloadRowsAtIndexPaths:@[indexPath, lastSelectedPath] ... I don't understand how the lastSelectedPath makes the tall cell appear, and hide the short cell. – thewheelz Apr 14 '13 at 22:22
  • @AlanMond, the lastSelectedIndexPath just keeps track of the previously selected row. When you reload the rows, you need to change both the newly selected one, and the one that was selected. The thing that causes one to be tall and the other short is just which row is the selected row (kept in the property selectedPath) -- the if statements in heightForRowAtIndexPath and cellForRowAtIndexPath determine the height of the row, and which cell (tall or short) is returned. – rdelmar Apr 14 '13 at 22:55
0

After doing a little more research, I found a different way to do something like what you want using just one cell. In this method, you have just the taller cell, but return a height that only lets you see the top half of the cell unless its selected. You have to do two things in the design of your cell to make this work. First select the box for "clip subviews" in IB, so the buttons won't show outside their view (the cell). Second, make the constraints such that the buttons are all lined up vertically with each other and give one of them a vertical spacing constraint to one of the UI elements in the top of the cell. Make sure none of the buttons has a constraint to the bottom of the cell. By doing this, the buttons will maintain a fixed distance with the top elements (which should have a fixed space to the top of the cell), which will cause them to be hidden when the cell is short. To animate the change in height, all you have to do is call beginUpdates followed by endUpdates on the table view.

#import "TableController.h"
#import "TallCell.h"

@interface TableController ()
@property (strong,nonatomic) NSArray *theData;
@property (strong,nonatomic) NSIndexPath *selectedPath;
@end

@implementation TableController 


- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"TallCell" bundle:nil] forCellReuseIdentifier:@"TallCell"];
    self.theData = @[@"One",@"Two",@"Three",@"Four",@"Five",@"Six",@"Seven",@"Eight",@"Nine"];
    [self.tableView reloadData];
}



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.theData.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath isEqual:self.selectedPath]) {
        return 88;
    }else{
        return 44;
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    TallCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TallCell" forIndexPath:indexPath];
    cell.label.text = self.theData[indexPath.row];
    return cell;
}


#pragma mark - Table view delegate

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

    if ([self.selectedPath isEqual:indexPath]) {
        self.selectedPath = nil;
    }else{
        self.selectedPath = indexPath;
    }

    [tableView beginUpdates];
    [tableView endUpdates];
}

This, I think gives you the desired animation when you pick the first cell, but you get a somewhat different animation if you then pick a cell below the currently selected one. I don't know of any way around that.

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • I was looking everywhere how to clip the subviews. Thanks for including that in your explanation! I am still trying to make this work by expanding cell height and still get the cell below to work just like the cell above it. – thewheelz Apr 21 '13 at 03:19
  • one way I found to solve it is by using the reloadRowsAtIndexpath method. Unfortunately, this also updates the cell. I wonder if we can just reload the beginupdate part of it without losing the row data.... – thewheelz Apr 21 '13 at 16:30