【问题标题】:Can you do custom animations for UITableView Cell Inserts?你可以为 UITableView Cell Inserts 做自定义动画吗?
【发布时间】:2025-12-24 06:05:07
【问题描述】:

我有一个由 NSFetchedResultsController 填充的 UITableView。

在表格的初始加载时,我希望在其中设置单元格动画,但我想做更多的自定义动画而不是

[tableView insertRowsAtIndexPaths:withRowAnimation:];

允许。具体来说,我想减慢动画的速度,并让它们以特定的方式从侧面飞入。我无法通过 UITableViewRowAnimation 常量来实现这一点。有没有办法使用 Core Animation 来做我想要的,或者我需要在这个特定实例中坚持使用 UIKit 动画?

感谢您的帮助!

乔尔

【问题讨论】:

    标签: objective-c cocoa-touch uitableview nsfetchedresultscontroller


    【解决方案1】:

    最新解决方案 (2017-12-12)

    添加 Swift 4.0 版本的 animate 方法。然后应该以与以下解决方案相同的方式实现:

    func animate() {
        for cell in self.tableView.visibleCells {
            cell.frame = CGRect(x: self.tableView.frame.size.width, y: cell.frame.origin.y, width: cell.frame.size.width, height: cell.frame.size.height)
            UIView.animate(withDuration: 1.0) {
                cell.frame = CGRect(x: 0, y: cell.frame.origin.y, width: cell.frame.size.width, height: cell.frame.size.height)
            }
        }
    }
    

    较新的解决方案 (2015-09-05)

    添加 Swift 2.0 版本的 animate 方法。然后应该以与以下解决方案相同的方式实现:

    func animate() {
        for cell in self.tableView.visibleCells {
            cell.frame = CGRectMake(320, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)
            UIView.animateWithDuration(1.0) {
                cell.frame = CGRectMake(0, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)
            }
        }
    }
    

    新解决方案 (2014-09-28)

    我对解决方案进行了一些修改,以使实施更容易,并使其适用于 iOS8。您需要做的就是在 TableViewController 中添加这个 animate 方法,并在您希望它动画时调用它(例如,在您的 reload 方法中,但您可以随时调用它):

    - (void)animate
    {
        [[self.tableView visibleCells] enumerateObjectsUsingBlock:^(UITableViewCell *cell, NSUInteger idx, BOOL *stop) {
            [cell setFrame:CGRectMake(320, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
            [UIView animateWithDuration:1 animations:^{
                [cell setFrame:CGRectMake(0, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
            }];
        }];
    }
    

    再次,根据您的喜好更改动画。此特定代码将以较慢的速度从右侧为单元格设置动画。

    旧解决方案 (2013-06-06)

    您可以通过实现自己的 UITableView 并覆盖 insertRowsAtIndexPaths 方法来做到这一点。这是一个示例,说明从右侧推动单元格的位置,非常缓慢(1 秒动画):

    - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
    {
        for (NSIndexPath *indexPath in indexPaths)
        {
            UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath];
            [cell setFrame:CGRectMake(320, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
    
            [UIView beginAnimations:NULL context:nil];
            [UIView setAnimationDuration:1];
            [cell setFrame:CGRectMake(0, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
            [UIView commitAnimations];
        }
    }
    

    您可以自己玩动画。表格视图不会自动调用此方法,因此您必须在表格视图委托中重写 reloadData 方法并自己调用此方法。

    评论

    reloadData 方法应该如下所示:

    - (void)reloadData
    {
        [super reloadData];
        NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
        for (int i = 0; i < [_data count]; i++)
            [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
        [self insertRowsAtIndexPaths:indexPaths withRowAnimation:0];
    }
    

    【讨论】:

    • 谢谢!像魅力一样工作。
    • 只是重写insertRowsAtIndexPaths:withRowAnimation: 方法吗?当我覆盖它然后使用beginUpdates:endUpdates: 方法调用时,应用程序在[UITableView _endCellAnimationsWithContext:] 上崩溃
    • 嗨。如果我尝试继承 UITableView 并覆盖 insertRowAtIndexPaths... 我的应用程序将崩溃并出现以下错误“更新后现有部分中包含的行 (30) 必须等于更新前该部分中包含的行数 (29) , 加上或减去从该节插入或删除的行数(0 插入,0 删除),加上或减去移入或移出该节的行数(0 移入,0 移出)。'" -- - 在我看来,在子类化之后,该行不再是真正插入的,因此不再一致。你明白为什么吗?
    • 你做了和我一样的reloadData方法吗(见我的编辑),问题是你不能在_data中插入另一个对象并调用insertRowAtIndexPaths,你需要这样做并做超级reloadData在你做动画之前确保类知道它是新数据。
    • 嗨,马特里克。好的,现在我明白了。严格来说,通常使用带有动画的 insertRow 而不调用 reloadData,所以这有点像破坏了 insertRow 的目的。因此,您的方法不必重写 insertRow,它本身可以是一个新方法。
    【解决方案2】:

    materik 的 answer 在 iOS 7.1 SDK 中对我不起作用,我在调用 [self cellForRowAtIndexPath:indexPath]; 后得到了 nil

    这是我在 UITableView 子类中的代码:

    - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths
                  withRowAnimation:(UITableViewRowAnimation)animation
    {
        [super insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
        [self endUpdates]; // Populates UITableView with data
        [self beginUpdates];
        for (NSIndexPath *indexPath in indexPaths) {
            __block UITableViewCell *cell = [super cellForRowAtIndexPath:indexPath];
            if (cell) { // If indexPath isn't visible we'll get nil here
                // Custom animation
                CGRect frame = cell.frame;
                frame.origin.x = cell.frame.size.width;
                cell.frame = frame;
                frame.origin.x = 0;
                void (^animationsBlock)() = ^{
                    cell.frame = frame;
                };
                if ([[UIView class] respondsToSelector:
                     @selector(animateWithDuration:delay:usingSpringWithDamping:
                               initialSpringVelocity:options:animations:completion:)]) {
                    [UIView animateWithDuration:0.3
                                          delay:0
                         usingSpringWithDamping:0.5
                          initialSpringVelocity:0
                                        options:0
                                     animations:animationsBlock
                                     completion:NULL];
                } else {
                    [UIView animateWithDuration:0.3
                                          delay:0
                                        options:UIViewAnimationOptionCurveEaseIn
                                     animations:animationsBlock
                                     completion:NULL];
                }
            }
        }
    }
    

    【讨论】:

    • 我在顶部插入行,但我在 [super cellForRowAtIndexPath:indexPath] 上得到单元格 nil 。是因为它还不可见,如果是,那么我该如何做插入动画
    • @harpreetSingh 抱歉,我已经有一段时间没有从事这个项目了。覆盖-[UITableView insertRowsAtIndexPaths:withRowAnimation:] 后,你得到nil 单元格吗?无论如何,我建议您尝试来自 updated upvoted answer 的方法,如果它不起作用 - 仅创建带有此问题的测试项目并在此处分享 - 包括我在内的某人可能能够检查它并帮助您解决此问题。
    • 我得到了修复。动画没有发生,因为我在它之后调用了 [tableView reloadData]。删除它之后它就像一个魅力。谢谢哥们
    【解决方案3】:

    使用这个:

    -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
             [cell setFrame:CGRectMake(320, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
                [UIView animateWithDuration:2 animations:^{
                    [cell setFrame:CGRectMake(100, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height)];
    
                }];
    
    }
    

    【讨论】:

    • 这种方法的问题在于,当用户滚动(不仅仅是插入)时,它总是会重新加载带有动画的可重用单元格。
    • @DavidRobertson 您可以将已动画单元格的数组保留在 tableview 或控制器中,并检查单元格是否已显示,如果没有,您可以进行插入动画。
    【解决方案4】:

    Swift 3.0 版本的 Mattias Eriksson 的答案

    func animate() {
        for cell in tblView.visibleCells {
            cell.frame = CGRect(x:320, y:cell.frame.origin.y, width:cell.frame.size.width, height:cell.frame.size.height)
                UIView.animate(withDuration: 1.0) {
                    cell.frame = CGRect(x:0, y:cell.frame.origin.y, width:cell.frame.size.width, height:cell.frame.size.height)
                }
        }
    }
    

    【讨论】:

      最近更新 更多