Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a toolbar between UITableView rows

I am interested on how tweetbot does the following:

Enter image description here

I would like to create the same thing with my app, where if you click on a row, it pops an additional UIToolBar and pressing on any other row will dismiss this view with animations.

The logic I think is simple, you just need to add a subView to the UITableViewCell when pressed and shift the rest of the content up, but how do you actually dismiss it when I press the other row?

like image 372
aherlambang Avatar asked May 10 '11 15:05

aherlambang


4 Answers

The best way to do this is to add a dummy cell below the cell that was tapped.

First you need to keep track of what cell is been tapped and act accordingly.

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

    //if user tapped the same row twice let's start getting rid of the control cell
    if([indexPath isEqual:self.tappedIndexPath]){
        [tableView deselectRowAtIndexPath:indexPath animated:NO];
    }

    //update the indexpath if needed... I explain this below 
    indexPath = [self modelIndexPathforIndexPath:indexPath];

    //pointer to delete the control cell
    NSIndexPath *indexPathToDelete = self.controlRowIndexPath;

    //if in fact I tapped the same row twice lets clear our tapping trackers 
    if([indexPath isEqual:self.tappedIndexPath]){
        self.tappedIndexPath = nil;
        self.controlRowIndexPath = nil;
    }
    //otherwise let's update them appropriately 
    else{
        self.tappedIndexPath = indexPath; //the row the user just tapped. 
        //Now I set the location of where I need to add the dummy cell 
        self.controlRowIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1   inSection:indexPath.section];
    }

    //all logic is done, lets start updating the table
    [tableView beginUpdates];

    //lets delete the control cell, either the user tapped the same row twice or tapped another row
    if(indexPathToDelete){
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] 
                          withRowAnimation:UITableViewRowAnimationNone];
    }
    //lets add the new control cell in the right place 
    if(self.controlRowIndexPath){
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath] 
                          withRowAnimation:UITableViewRowAnimationNone];
    }

    //and we are done... 
    [tableView endUpdates];  
} 

Whenever you have that dummy cell present you have to make sure to send the correct count.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if(self.controlRowIndexPath){
        return modelArray.count + 1;
    }
    return self.modelArray.count;
}

Also, return the appropriate height for your ControlCell.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath   *)indexPath 
{
    if([indexPath isEqual:self.controlRowIndexPath]){
        return 45; //height for control cell
    }
    return 70; //height for every other cell 
}

Lastly, remember the control cell is a dummy. Is not part of the model, thus you have to account for that. If the user taps a row that is above the last tapped row is ok but when the new tapped row is below that control cell you have to make sure you access the right row in your model. In other words, account for that fake cell in the middle of your view.

- (NSIndexPath *)modelIndexPathforIndexPath:(NSIndexPath *)indexPath
{
    int whereIsTheControlRow = self.controlRowIndexPath.row;
    if(self.controlRowIndexPath != nil && indexPath.row > whereIsTheControlRow)
        return [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0]; 
    return indexPath;
}

I hope this helps.

like image 73
rjgonzo Avatar answered Nov 14 '22 17:11

rjgonzo


In tableView:didSelectRowAtIndexPath:, you remove the tool view from the last selected cell. If there is no such view, you create a new one. Then you add this view to the newly selected cell. Save the indexPath of the selected row.

In tableView:heightForRowAtIndexPath:, you check if the indexPath is the same as the saved indexPath. If they are equal, you return a height that is the height of both views. If it is not equal, just return the height of the "real cell".

Put all your calls in didSelectRowAtIndexPath between [tableView beginUpdates] and [tableView endUpdates] to get animation for the height change.

like image 38
Matthias Bauch Avatar answered Nov 14 '22 17:11

Matthias Bauch


rjgonzo's code works fine except for the case where you only have 1 row in the tableview. When there's only 1 row (and 1 object in the tableview datamodel) you'll get an NSRange exception when you call insertRowsatIndexPath(s). To fix this I checked to see if the datamodel has only 1 object and if so then I add the row to the current indexpath (instead of the controlindexpath) which results in the row logically being added above the first row and then I call moveRowAtIndexPath to interchange the rows after calling [self.tableView endUpdates]. The animation shows as expected with the control row appearing to slide down from the 1st row.

if(self.controlRowIndexPath){

//Check to see if the datamodel only has 1 object

 if([self.objects count]==1){
//If so then insert the row above the row
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                              withRowAnimation:UITableViewRowAnimationNone];

    }
    else
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath]
                          withRowAnimation:UITableViewRowAnimationNone];

 }

[self.tableView endUpdates];

//if the row was inserted above the 1st row then switch the rows

 if([self.objects count]==1)
 [self.tableView moveRowAtIndexPath:self.controlRowIndexPath toIndexPath:indexPath];
like image 2
user2016783 Avatar answered Nov 14 '22 19:11

user2016783


I would not add a subview to a UITableViewCell, I would add another row to the UITableView. That way, the UITableView will take care of the animation. (And I don't think that's possible to animated UITableViewCell height changes...)

Use simply

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

do add a row. And

- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

to remove it.

like image 1
gcamp Avatar answered Nov 14 '22 19:11

gcamp