如何在UITableView行之间创建工具栏

时间:2011-05-10 15:40:32

标签: iphone objective-c user-interface uitableview

我对tweetbot如何执行以下操作感兴趣:

Enter image description here

我想用我的应用程序创建相同的东西,如果你点击一行,它会弹出 另外一个UIToolBar并按下任何其他行将使用动画关闭此视图。

我认为逻辑很简单,你只需要在按下时将一个子视图添加到UITableViewCell并将其余部分向上移动,但是当我按下另一行时你如何实际解除它?

6 个答案:

答案 0 :(得分:29)

执行此操作的最佳方法是在轻敲的单元格下方添加虚拟单元格。

首先,您需要跟踪被挖掘的细胞并采取相应措施。

- (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];  
} 

每当你有虚拟细胞存在时,你必须确保发送正确的计数。

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

此外,返回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 
}

最后,请记住控制单元是假的。不是模型的一部分,因此您必须考虑到这一点。如果用户点击最后一个点击行上方的行是正常的,但是当新点击的行位于该控制单元格下方时,您必须确保访问模型中的右侧行。换句话说,在视图中间考虑该假单元格。

- (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;
}

我希望这会有所帮助。

答案 1 :(得分:18)

tableView:didSelectRowAtIndexPath:中,您从最后选择的单元格中删除工具视图。如果没有这样的视图,则创建一个新视图。然后将此视图添加到新选择的单元格中。保存所选行的indexPath。

tableView:heightForRowAtIndexPath:中,检查indexPath是否与保存的indexPath相同。如果它们相等,则返回高度,即两个视图的高度。如果它不相等,只需返回“真实单元格”的高度。

didSelectRowAtIndexPath之间的所有来电置于[tableView beginUpdates][tableView endUpdates]之间,以获取高度变化的动画。

答案 2 :(得分:2)

rjgonzo的代码工作正常,除了在tableview中只有1行的情况。当只有1行(和tableview datamodel中的1个对象)时,在调用insertRowsatIndexPath(s)时会得到NSRange异常。为了解决这个问题,我检查了数据模型是否只有1个对象,如果是,那么我将该行添加到当前索引路径(而不是controlindexpath),这会导致逻辑上将行添加到第一行之上,然后调用moveRowAtIndexPath调用[self.tableView endUpdates]后交换行。动画显示为预期,控件行似乎从第1行向下滑动。

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];

答案 3 :(得分:1)

我不会向UITableViewCell添加子视图,我会向UITableView添加另一行。这样,UITableView将处理动画。 (而且我认为动画UITableViewCell高度变化不可能......)

简单地使用

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

添加一行。和

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

删除它。

答案 4 :(得分:0)

这就是我正在做的让动画变得干净。

除了策略用户 fluchtpunkt 建议(即向子单元添加子视图,通过heightForRowAtIndexPath,beginUpdates和endUpdates更新单元格高度),我发现以下措施:对动画很有帮助:

  1. tableviewcells有一个背景图片。否则,在tableview动画高度变化之前,通过单元格可以看到添加的子视图/工具栏。
  2. tableviewcell位于其下方单元格视图的“后面”,否则子视图/工具栏将再次显示。我正在使用[tableview sendSubviewToBack:cell];并且正在处理这个问题。
  3. 这对我来说很干净,但与Tweetbot不完全相同。有趣的是,Tweetbot的动画似乎会拉低工具栏,因为单元格的底部会动画下来。似乎必须进行一些额外的动画,或者我的阴谋理论是它实际上将子视图添加到所选单元格下方的单元格顶部,然后在下面的单元格上执行延长和动画。

答案 5 :(得分:0)

@rjgonzo这很好但但是如何保留indexPathToDelete存在一个小问题。因为它只是指向self.controlRowIndexPath的另一个指针,所以一旦你清除或重新分配self.controlRowIndexPath,indexPathToDelete将不是你想要的,并且tableView deleteRowsAtIndexPaths:withRowAnimation:call,你将得到一个SIGBART崩溃。

所以,而不是

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

    //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];
    }

以下代码应该可以正常工作:

    //pointer to delete the control cell
NSIndexPath *indexPathToDelete = [NSIndexPath indexPathForRow:self.control_row_index_path.row inSection:self.control_row_index_path.section];
    ...
    ...
    //lets delete the control cell, either the user tapped the same row twice or tapped another row
if(indexPathToDelete.row != 0){
    NSLog(@"row to delete %d", indexPathToDelete.row);
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] withRowAnimation:UITableViewRowAnimationNone];
}