【问题标题】:Animation when inserting a larger row into a UITableView assuming the wrong height假设高度错误,将较大的行插入 UITableView 时的动画
【发布时间】:2013-11-28 06:40:54
【问题描述】:

我在动画添加或删除 UITableView 中的行时遇到问题,该行的高度与其他行不同。

以下 GIF 演示了默认高度 (44pts) 的行和插入和删除更大行 (100pts) 的问题。左边是模拟器的屏幕录像(新单元格最终覆盖第五行是一个不同的问题),右边是它应该做什么的模型。

就我而言,我有一堆行,每行 60pts 高。当点击单元格中的按钮时,“编辑”单元格将从下方滑出,将下方的单元格向下推。此编辑单元格高 180 点。当我调用 insertRowsAtIndexPaths:withRowAnimation:deleteRowsAtIndexPaths:withRowAnimation: 时,动画假定错误的高度为 60pts,而不是 180pts,它应该是
这意味着在UITableViewRowAnimationTop 的情况下,新单元格出现在距离它最终位置的-60pts 处,并向下滑动到新位置;大约三分之一的动画应该做。同时,下面的行从其起始位置平滑地动画到向下 180 点,完全符合其应有的效果。

有没有人为此制定了实际的解决方案?有什么方法可以告诉新行动画应该是什么高度?

下面是我用来隐藏和显示编辑行的代码。我使用TLSwipeForOptionsCell 来触发编辑,但它很容易复制使用例如tableView:didSelectRowAtIndexPath:

-(void)hideEditFields{
    [self.tableView beginUpdates];

    [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForItem:editFormVisibleForRow+1 inSection:0]] withRowAnimation:UITableViewRowAnimationTop];
    editFormVisibleForRow = -1;

    [self.tableView endUpdates];
}

-(void)cellDidSelectMore:(TLSwipeForOptionsCell *)cell{
    NSIndexPath* indexPath = [self.tableView indexPathForCell:cell];
    // do nothing if this is the currently selected row
    if(editFormVisibleForRow != indexPath.row){
        if(editFormVisibleForRow >= 0){
            [self hideEditFields];
            // update the index path, as the cell positions (may) have changed
            indexPath = [self.tableView indexPathForCell:cell];
        }

        [self.tableView beginUpdates];

        editFormVisibleForRow = indexPath.row;
        [self.tableView insertRowsAtIndexPaths:@[
                                             [NSIndexPath indexPathForItem:editFormVisibleForRow+1 inSection:0]
                                             ] withRowAnimation:UITableViewRowAnimationTop];

        [self.tableView endUpdates];
    }
}

-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _dataSource.count + (editFormVisibleForRow >= 0 ? 1 : 0);
}

-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    int row = indexPath.row;
    if(editFormVisibleForRow >= 0 && row > editFormVisibleForRow && row <= editFormVisibleForRow + 1){
        return 180.0f;
    }
    else return 60.0;
}

四处寻找,似乎这是一个没有明确答案的常见问题。我在这里找到的关于 SO 的大多数类似问题都没有得到解答,或者提供了针对提问者情况的解决方法。 (例如:Problem with RowAnimationCustom UITableViewCell height yields improper animationUITableView animation glitch when deleting and inserting cells with varying heights)。

另外,我没有尝试制作一个三倍大小的编辑行,而是尝试制作三个较小的行并为它们设置动画,但这并不合适,因为它们都同时出现。我还尝试一个接一个地为它们设置动画,但缓动让它看起来很奇怪,出现了一个明显的 3 步动画,而不是整个编辑视图在一个动作中滑出视图。

编辑:我刚刚注意到,如果我为我要制作动画的那一行上方的行调用reloadRowsAtIndexPaths:withRowAnimation: UITableViewRowAnimationNone,它会改变动画的行为;即动画假设高度为0pts,如下动画所示。它更接近我想要的,但仍然不正确,因为动画速度是错误的并且留下了差距(在我的应用程序中,这意味着背景 颜色戳穿)

【问题讨论】:

  • 你想明白了吗?尝试在这里做类似的事情......谢谢
  • 不幸的是,客户改变了我们的设计,不再需要这个功能,所以我从来没有解决过。如果我再次遇到它并解决它,我一定会回答这个问题
  • 遇到同样的问题
  • 我遇到了同样的问题。有人解决了这个问题吗?
  • 所以没有解决方法?从现在开始 2 年了?

标签: ios animation uitableview


【解决方案1】:

这个,使用 didSelectRowAtIndexPath 对我有用:

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

@implementation TableController 


- (void)viewDidLoad {
    [super viewDidLoad];
    self.theData = @[@"One",@"Two",@"Three",@"Four",@"Five",@"Six",@"Seven",@"Eight",@"Nine"];
    [self.tableView reloadData];
}


-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return ([indexPath isEqual:self.pathToEditCell])? 100: 44;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return (self.pathToEditCell == nil)? self.theData.count: self.theData.count + 1;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath isEqual:self.pathToEditCell]) {
        RDEditCell *cell = [tableView dequeueReusableCellWithIdentifier:@"EditCell" forIndexPath:indexPath];
        return cell;
    }else{
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
        cell.textLabel.text = self.theData[indexPath.row];
        return cell;
    }
}


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

    if (self.pathToEditCell == nil) { // first time selecting a row
        self.pathToEditCell = [NSIndexPath indexPathForRow:indexPath.row +1 inSection:indexPath.section];
        [self.tableView insertRowsAtIndexPaths:@[self.pathToEditCell] withRowAnimation:UITableViewRowAnimationAutomatic];
    }else if ([self.pathToEditCell isEqual:indexPath]){ // deletes the edit cell if you click on it
        self.pathToEditCell = nil;
        [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];

    }else{ // close the old edit cell and adds another if you click on another cell while the edit cell is on screen
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[self.pathToEditCell] withRowAnimation:UITableViewRowAnimationFade];
        self.pathToEditCell = indexPath;
        [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.tableView endUpdates];
    }
}

对于删除,我喜欢动画的“淡入淡出”选项的外观,但“顶部”也可以。

【讨论】:

  • 您的代码也有同样的问题。编辑单元格仅从 -44pts 滑出,而不是 100pts,这使得单元格看起来像是从中心扩展而不是向下滑动。这是一个演示问题的 gif:i34.photobucket.com/albums/d135/death_au/cell-anim-issue.gif
  • @death_au,好的,我想我误解了你的问题——我以为你只看到了新细胞的一部分。就个人而言,我认为该动画没有任何问题——我觉得它还可以。我什至没有注意到你在说什么,直到我放慢了速度。无论如何,我不知道如何解决这个问题。
【解决方案2】:

解决方案非常简单。您需要插入高度为0的单元格,然后将高度更改为预期的大小,然后调用beginUpdatesendUpdates

这是一些伪代码。

var cellHeight: CGFloat = 0

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let dynamicHeightIndex = 2
    if indexPath.row == dynamicHeightIndex {
        return cellHeight
    } else {
        return tableView.rowHeight
    }
}

func insertCell() {
    // First update the data source before inserting the row
    tableView.insertRows(at: [someIndexPath], with: .none)

    cellHeight = 200

    tableView.beginUpdates()
    tableView.endUpdates()
}

要删除单元格,您需要等到更新动画完成后才能从表格视图中删除。

在 iOS 11 中,您有 func performBatchUpdates(_:completion:),它提供了一个完成块。对于以前的版本,您可以尝试使用 CATransaction 补全。

cellHeight = 0

CATransaction.begin()
CATransaction.setCompletionBlock({
    self.tableView.deleteRows(at: [someIndexPath], with: .none)
})

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-26
    • 2015-08-01
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    相关资源
    最近更新 更多