我想将与动画单元格插入一起运行的动画中的UICollectionView
的帧大小更改为performBatchUpdates:completion:
块内的同一个集合视图。
这是触发单元格插入的代码:
[collectionView performBatchUpdates:^{
indexPathOfAddedCell = ...;
[collectionView insertItemsAtIndexPaths:@[ indexPathOfAddedCell ]];
} completion:nil];
因为单元格插入会导致集合视图的contentSize
发生变化,所以我尝试注册KVO以更改该属性,然后从KVO处理程序触发集合视图框架更新。
这种方法的问题是contentSize
的KVO触发器触发得太晚了:当时单元插入动画已经完成(实际上,KVO在performBatchUpdates:completion:
的完成处理程序获得之前触发在动画播放完UI之后调用了。)
我没有使用自动布局。
编辑:我将a sample project to demonstrate my problem放在GitHub上。
编辑2:我应该提到我需要这个我正在编写的组件(OLEContainerScrollView
),该组件应该100%独立于集合视图。因此,我不能对集合视图布局进行子类化,也不会影响触发单元格动画的代码。理想情况下,解决方案也适用于UITableView
,它表现出相同的行为。
答案 0 :(得分:10)
我查看了集合视图和表视图如何更新其内容插入,实际上,滚动视图的内容大小仅在动画完成后才更新。如果不使用私有API,似乎没有一种非常好的方法来监听未来的内容大小,但它是可能的。
对于表格视图,动画开始的触发器为-[UITableView _endCellAnimationsWithContext:]
。此方法设置所需的所有动画(仅用于将来的可见单元格),执行它们并设置最终调用-[UITableView _updateContentSize]
的完成块。 _updateContentSize
使用内部-[UITableView _contentSize]
方法设置正确的滚动视图内容大小。由于_endCellAnimationsWithContext:
仅处理动画,因此表格视图背后的数据已更新,因此调用_contentSize
(或使用valueForKey:@"_contentSize"
)会返回正确的大小。
集合视图非常相似。触发器为-[UICollectionView _endItemAnimations]
,它会为每个单元格,页眉和页脚启动许多动画,当所有动画完成后,-[UICollectionView _updateAnimationDidStop:finished:context:]
会设置正确的内容大小。由于这是一个集合视图,因此其集合视图布局实际上知道目标内容大小,因此您可以调用-[UICollectionViewLayout collectionViewContentSize]
来获取更新的内容大小。
这些都不是在应用商店中使用的好选择。我可以想到的一个选项是添加每个滚动视图子类的ISA调度,并包装所有可动画的入口点,跟踪它们是否被批处理,并且在批处理结束时或在独立动画操作结束时,使用获取目标内容大小的相应方法(-[UITableView _contentSize]
和-[UICollectionViewLayout collectionViewContentSize]
)。
原始答案,以防您想要了解自己的集合视图中集合视图大小的更改:
对集合视图布局进行子类化(如果尚未使用),并使用通知中心或委托方法,请在prepareForAnimatedBoundsChange:
上通知其他动画。它们将被添加到动画块中。
来自文档:
您还可以使用此方法执行其他动画。任何 您创建的动画将添加到用于处理的动画块中 插入,删除和界限的变化。
您可能需要确定更改内容,并仅通知插入动画。
答案 1 :(得分:7)
我已经查看了你的demo project,我认为不需要KVO。 如果你想在插入新单元时用动画改变集合视图的框架,那么我 认为你可以这样做:
#import "ViewController.h"
@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet UIView *otherView;
@property (nonatomic) NSInteger numberOfItemsInCollectionView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.numberOfItemsInCollectionView = 1; // This is our model
}
- (IBAction)addItem:(id)sender
{
// Change the model
self.numberOfItemsInCollectionView += 1;
[UIView animateWithDuration:0.24 animations:^{
NSIndexPath *indexPathOfInsertedCell = [NSIndexPath indexPathForItem:self.numberOfItemsInCollectionView - 1 inSection:0];
[self.collectionView insertItemsAtIndexPaths:@[ indexPathOfInsertedCell ]];
CGRect collectionViewFrame = self.collectionView.frame;
collectionViewFrame.size.height = (self.numberOfItemsInCollectionView * 40) + 94;
self.collectionView.frame = collectionViewFrame;
}];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.numberOfItemsInCollectionView;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
return cell;
}
@end
或者如果你想要淡化效果:
- (IBAction)addItem:(id)sender
{
// Change the model
self.numberOfItemsInCollectionView += 1;
CATransition *transition = [CATransition animation];
transition.type = kCATransitionFade;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.fillMode = kCAFillModeForwards;
transition.duration = 0.5;
[[self.collectionView layer] addAnimation:transition forKey:@"UICollectionViewInsertRowAnimationKey"];
NSIndexPath *indexPathOfInsertedCell = [NSIndexPath indexPathForItem:self.numberOfItemsInCollectionView - 1 inSection:0];
[self.collectionView insertItemsAtIndexPaths:@[ indexPathOfInsertedCell ]];
CGRect collectionViewFrame = self.collectionView.frame;
collectionViewFrame.size.height = (self.numberOfItemsInCollectionView * 40) + 94;
self.collectionView.frame = collectionViewFrame;
}
我已经通过您的演示项目检查了这个,它的作用对我而言。 如果它也适合你,请试试这个并发表评论。
答案 2 :(得分:-1)
为什么不简单地添加动画块来更改集合视图的框架?
这会产生一个动画边界变化,与插入淡入淡出一起很好。
此解决方案不需要KVO材料。
编辑:您可以在更改建模时触发该代码
以下是您的示例项目的相关代码:
- (IBAction)addItem:(id)sender
{
// Change the model
self.numberOfItemsInCollectionView += 1;
// Animate cell insertion
[self.collectionView performBatchUpdates:^{
NSIndexPath *indexPathOfInsertedCell = [NSIndexPath indexPathForItem:self.numberOfItemsInCollectionView - 1 inSection:0];
[self.collectionView insertItemsAtIndexPaths:@[ indexPathOfInsertedCell ]];
} completion:nil];
// animate the collection view's frame
[UIView animateWithDuration:.5
animations:^{
CGRect collectionViewFrame = self.collectionView.frame;
collectionViewFrame.size.height = (self.numberOfItemsInCollectionView * 40) + 94;
self.collectionView.frame = collectionViewFrame;
}];
}