更改时为UITableView设置动画的reloadData


183

我有一个有两种模式的UITableView。当我们在模式之间切换时,我有不同数量的节和每个节的单元格。理想情况下,当表格增加或缩小时,它将执行一些很酷的动画处理。

这是我尝试的代码,但是它什么也没做:

CGContextRef context = UIGraphicsGetCurrentContext(); 
[UIView beginAnimations:nil context:context]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 
[UIView setAnimationDuration:0.5]; 

[self.tableView reloadData];
[UIView commitAnimations];

关于如何执行此操作的任何想法?

Answers:


400

实际上,这非常简单:

[_tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

文档中

调用此方法会使表视图向其数据源询问指定部分的新单元格。表格视图将新单元格的插入动画化,同时将旧单元格动画化。


13
而仅仅是此页面上的最佳答案!
Matthias D

40
这不是完全正确的,因为它只会重新加载第一部分。如果您的表有多个部分,将无法使用
Nosrettap 2013年

4
这是可能的,如果没有数据发生了变化,你会得到一个异常..见我的答案
马捷

8
@Nosrettap:如果要重新加载tableView的更多或所有部分,您要做的就是用所有部分索引扩展NSIndexSet,这也需要刷新
JonEasy 2015年

Swift版本:_tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)但是,值得注意的是,它仅更新第0部分,这是默认的第一个部分。
kbpontius '16

262

您可能要使用:

物镜

[UIView transitionWithView: self.tableView
                  duration: 0.35f
                   options: UIViewAnimationOptionTransitionCrossDissolve
                animations: ^(void)
 {
      [self.tableView reloadData];
 }
                completion: nil];

迅速

UIView.transitionWithView(tableView,
                          duration: 0.35,
                          options: .TransitionCrossDissolve,
                          animations:
{ () -> Void in
    self.tableView.reloadData()
},
                          completion: nil);

斯威夫特3、4和5

UIView.transition(with: tableView,
                  duration: 0.35,
                  options: .transitionCrossDissolve,
                  animations: { self.tableView.reloadData() }) // left out the unnecessary syntax in the completion block and the optional completion parameter

没有麻烦。:D

您还可以使用任何UIViewAnimationOptionTransitions您想要的效果更酷的效果:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom

2
如果表视图的开始/结束状态会非常不同(这很复杂,计算要添加/删除的节和行会很复杂),则此方法很有用,但是您要避免产生非动画重载所带来的麻烦。
Ben Packard

1
这不像使用内置方法重新加载tableview那样好看,但是与这里提到的更高评分的方法不同,当您有多个部分时,此方法可以工作。
马克·布里奇斯

1
哇,尝试了所有其他方法后,这对我来说绝对是完美的
卢卡斯·古森

1
@MarkBridges评分较高的答案确实适用于多个部分:)[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
lobianco 2014年

2
@Anthony如果结束状态具有比开始状态更多/更少的部分,则不起作用。然后,您必须手动跟踪添加/删除了哪些部分,这很麻烦。
nikolovski 2014年

78

CATransition上课有更多的自由。

它不仅限于淡入淡出,还可以移动。


例如:

(不要忘记导入QuartzCore

CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.fillMode = kCAFillModeForwards;
transition.duration = 0.5;
transition.subtype = kCATransitionFromBottom;

[[self.tableView layer] addAnimation:transition forKey:@"UITableViewReloadDataAnimationKey"];

更改type以满足您的需求,例如kCATransitionFade

在Swift中实现:

let transition = CATransition()
transition.type = kCATransitionPush
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.fillMode = kCAFillModeForwards
transition.duration = 0.5
transition.subtype = kCATransitionFromTop
self.tableView.layer.addAnimation(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()

参考 CATransition

斯威夫特5:

let transition = CATransition()
transition.type = CATransitionType.push
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.fillMode = CAMediaTimingFillMode.forwards
transition.duration = 0.5
transition.subtype = CATransitionSubtype.fromTop
self.tableView.layer.add(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()

这将整个表格动画化,而不是单个行/节。
阿戈斯

@Agos它仍然可以回答问题。“如何仅重新加载一行”问题有一个完全不同的答案,例如将动画应用于的layer属性UITableViewCell
2013年

@matejkramny我希望该表仅对不同的行设置动画(如问题中所引用),但是此方法可一次推送所有表。也许我缺少什么?
阿戈斯

@Agos嗯,不,它应该这样做。由于QuartzCore直接修改视图,因此它不能只处理一半的表。您可以尝试从表格视图中获取单元格,然后将其应用于每个动画(但不确定是否可以正常工作)
Matej

2
通过kCATransitionFade就像魅力一样工作谢谢!:)
quarezz

60

我相信您可以只更新数据结构,然后:

[tableView beginUpdates];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView endUpdates];

另外,“ withRowAnimation”也不是布尔值,而是一种动画样式:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle

简单的东西,但是比拖网文档更容易直接进入StackOverflow并获取所需的摘录。。谢谢!
Jasper Blues

24

所有这些答案都假定您使用的UITableView只有一个部分。

要准确处理您拥有多个部分的情况,请使用:

NSRange range = NSMakeRange(0, myTableView.numberOfSections);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
[myTableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];

(注意:您应确保您拥有0个以上的部分!)

要注意的另一件事是,如果您尝试同时使用此代码更新数据源,则可能会遇到NSInternalInconsistencyException。在这种情况下,可以使用类似于以下的逻辑:

int sectionNumber = 0; //Note that your section may be different

int nextIndex = [currentItems count]; //starting index of newly added items

[myTableView beginUpdates];

for (NSObject *item in itemsToAdd) {
    //Add the item to the data source
    [currentItems addObject:item];

    //Add the item to the table view
    NSIndexPath *path = [NSIndexPath indexPathForRow:nextIndex++ inSection:sectionNumber];
    [myTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationAutomatic];
}

[myTableView endUpdates];

3
关于节的计算很重要,但是我只有一个节,并且您的代码有一个错误的错误。我必须将代码的第一行更改为NSRange range = NSMakeRange(0,myTableView.numberOfSections);
Danyal Aytekin

18

解决此问题的方法是告诉tableView使用以下命令删除和添加行和节:

insertRowsAtIndexPaths:withRowAnimation:
deleteRowsAtIndexPaths:withRowAnimation:
insertSections:withRowAnimation:
deleteSections:withRowAnimation:

UITableView的方法。

调用这些方法时,表格将为所需的项设置动画,然后对其自身调用reloadData,以便您可以在此动画之后更新状态。这部分很重要-如果您动画化所有内容但不更改表的dataSource返回的数据,则动画完成后,行将再次出现。

因此,您的应用程序流程将是:

[self setTableIsInSecondState:YES];

[myTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES]];

只要您表的dataSource方法通过检查[self tableIsInSecondState](或其他方法)返回正确的新节和行集,这将达到您想要的效果。


16

我无法对最佳答案发表评论,但是快速的实现方法是:

self.tableView.reloadSections([0], with: UITableViewRowAnimation.fade)

您可以在reloadSections的第一个参数中包含要更新的部分。

可从文档中获取其他动画:https : //developer.apple.com/reference/uikit/uitableviewrowanimation

淡入淡出 插入或删除的一个或多个行淡入或淡出表视图。

右侧 插入的行从右侧滑入;删除的行会向右滑动。

左侧 插入的行从左侧滑入;删除的行或多个行向左滑动。

顶部 插入的一行或多行中从顶部滑动; 删除的行或多行滑向顶部。

底部 插入的行从底部滑入;删除的行会滑向底部。

情况无 插入或删除的行使用默认动画。

中间 表格视图试图将新旧单元格保持在它们曾经或将要占据的空间的中心。在iPhone 3.2中可用。

自动 表格视图为您选择适当的动画样式。(在iOS 5.0中引入。)


1
对于Swift 4.2:self.tableView.reloadSections([0],带有:UITableView.RowAnimation.fade)
Reefwing

14

@dmarnel答案的Swift 4版本:

tableView.reloadSections(IndexSet(integer: 0), with: .automatic)

9

快速实施:

let range = NSMakeRange(0, self.tableView!.numberOfSections())
let indexSet = NSIndexSet(indexesInRange: range)
self.tableView!.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.Automatic)

8

对于Swift 4

tableView.reloadSections([0], with: UITableView.RowAnimation.fade)

1

就我而言,我想在表视图中添加10行(用于“显示更多结果”类型的功能),并执行以下操作:

  NSInteger tempNumber = self.numberOfRows;
  self.numberOfRows += 10;
  NSMutableArray *arrayOfIndexPaths = [[NSMutableArray alloc] init];
  for (NSInteger i = tempNumber; i < self.numberOfRows; i++) {
    [arrayOfIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
  }
  [self.tableView beginUpdates];
  [self.tableView insertRowsAtIndexPaths:arrayOfIndexPaths withRowAnimation:UITableViewRowAnimationTop];
  [self.tableView endUpdates];

在大多数情况下,通常会使用对象数组的计数代替tableself的“ self.numberOfRows”。因此,为确保该解决方案对您而言效果良好,“ arrayOfIndexPaths”必须是要插入的行的索引路径的精确数组。如果该索引路径中的任何行均存在,则代码可能会崩溃,因此对于那些索引路径,应使用方法“ reloadRowsAtIndexPaths:withRowAnimation:”来避免崩溃


1

如果要将自己的自定义动画添加到UITableView单元格,请使用

[theTableView reloadData];
[theTableView layoutSubviews];
NSArray* visibleViews = [theTableView visibleCells];

以获得可见细胞的阵列。然后将任何自定义动画添加到每个单元格。

看看我张贴的这个要点,以获得流畅的自定义单元格动画。 https://gist.github.com/floprr/1b7a58e4a18449d962bd


1

Swift中没有reloadData()的动画可以这样完成(从2.2版开始):

tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
    // ...or here, if you need to add/remove the same amount of objects to/from somewhere
    indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
    numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()

如果动画结束后需要调用reloadData(),可以在CATransaction中进行如下更改:

CATransaction.begin()
CATransaction.setCompletionBlock({() in self.tableview.reloadData() })
tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove.count ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
     // ...or here, if you need to add/remove the same amount of objects to/from somewhere
     indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
     numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()
CATransaction.commit()

删除行时会显示此逻辑,但相同的想法也适用于添加行。您还可以将动画更改为UITableViewRowAnimation.Left使其整洁,或从其他可用动画列表中选择。


1
CATransition *animation = [CATransition animation];
animation.duration = .3;
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setDuration:.3];

[[_elementTableView layer] addAnimation:animation forKey:@"UITableViewReloadDataAnimationKey"];

[tableView reloadData];

2
为什么要设置.3两次持续时间?

1

重新加载的所有部分,而不仅仅是一个与自定义工期

用于设置自定义持续时间的用户duration参数UIView.animate

UIView.animate(withDuration: 0.4, animations: { [weak self] in
    guard let `self` = self else { return }
    let indexSet = IndexSet(integersIn: 0..<self.tableView.numberOfSections)
    self.tableView.reloadSections(indexSet, with: UITableView.RowAnimation.fade)
})

0

UITableViewSwift中的本机动画

一次插入和删除所有行,tableView.performBatchUpdates以便它们同时发生。以@iKenndac的答案为基础,使用以下方法:

  • tableView.insertSections
  • tableView.insertRows
  • tableView.deleteSections
  • tableView.deleteRows

例如:

 tableView.performBatchUpdates({
   tableView.insertSections([0], with: .top)
 })

这将在零位置插入一个节,并从顶部加载一个动画。这将重新运行cellForRowAt方法,并在该位置检查新的单元格。您可以使用特定的动画以这种方式重新加载整个表格视图。

关于:OP问题,将需要一个条件标志来显示备用表视图状态的单元格。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.