66

我想要做的是更改 UICollectionViewCell 的大小,并在选择单元格时为该更改设置动画。我已经设法通过将单元格标记为已选中collectionView: didSelectItemAtIndexPath:然后调用reloadData我的 UICollectionView 以不同大小显示所选单元格来做到这一点。

然而,这一切都是同时发生的,我不知道如何让大小的变化变成动画。有任何想法吗?

我已经在 selection 上找到了 Animate uicollectionview 单元格,但答案对我来说是不确定的,我还没有弄清楚它是否也可以帮助我。

4

10 回答 10

89

在 Chase Roberts 的帮助下,我现在找到了解决问题的方法。我的代码基本上是这样的:

// Prepare for animation

[self.collectionView.collectionViewLayout invalidateLayout];
UICollectionViewCell *__weak cell = [self.collectionView cellForItemAtIndexPath:indexPath]; // Avoid retain cycles
void (^animateChangeWidth)() = ^()
{
    CGRect frame = cell.frame;
    frame.size = cell.intrinsicContentSize;
    cell.frame = frame;
};

// Animate

[UIView transitionWithView:cellToChangeSize duration:0.1f options: UIViewAnimationOptionCurveLinear animations:animateChangeWidth completion:nil];

进一步解释:

  1. 最重要的步骤之一是对您正在使用的invalidateLayout调用。UICollectionView.collectionViewLayout如果你忘记了它,细胞很可能会在你的集合边界上生长——这是你很可能不希望发生的事情。还要考虑您的代码应该在某些时候将单元格的新大小呈现给您的布局。就我而言(我使用的是UICollectionViewFlowLayout),我调整了collectionView:layout:sizeForItemAtIndexPath方法以反映修改后的单元格的新大小。invalidateLayout忘记那部分,如果您首先调用然后尝试为大小的变化设置动画,那么您的单元格大小根本不会发生任何事情。

  2. 最奇怪(至少对我来说)但重要的是你invalidateLayout 不在动画块内调用。如果这样做,动画将不会显示流畅,而是会出现故障和闪烁。

  3. 您必须使用单元格作为参数调用UIViewstransitionWithView:duration:options:animations:completion:方法transitionWithView,而不是整个集合视图,因为它是您想要动画的单元格,而不是整个集合。

  4. 我使用intrinsicContentSize单元格的方法来返回我想要的大小 - 在我的情况下,我会根据单元格的选择状态来扩大和缩小单元格的宽度以显示按钮或隐藏它。

我希望这会帮助一些人。对我来说,我花了好几个小时才弄清楚如何让动画正常工作,所以我试图让其他人摆脱这种耗时的负担。

于 2012-12-08T22:58:28.957 回答
51

对单元格的大小进行动画处理对集合视图的工作方式与对表格视图的工作方式相同。如果您想为单元格的大小设置动画,请拥有一个反映单元格状态的数据模型(在我的情况下,我只使用一个标志 (CollectionViewCellExpanded, CollectionViewCellCollapsed) 并相应地返回一个 CGSize。

当您调用并清空 performBathUpdates 调用时,视图会自动生成动画。

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [collectionView performBatchUpdates:^{
        // append your data model to return a larger size for
        // cell at this index path
    } completion:^(BOOL finished) {

    }];
}
于 2013-02-11T20:29:46.577 回答
31

-setCollectionViewLayout:animated:通过使用创建相同集合视图布局的新实例,我已经成功地为更改设置动画。

例如,如果您使用的是UICollectionViewFlowLayout,那么:

// Make sure that your datasource/delegate are up-to-date first

// Then animate the layout changes
[collectionView setCollectionViewLayout:[[UICollectionViewFlowLayout alloc] init] animated:YES];
于 2013-06-16T22:44:47.197 回答
22
[UIView transitionWithView:collectionView 
                  duration:.5 
                   options:UIViewAnimationOptionTransitionCurlUp 
                animations:^{

    //any animatable attribute here.
    cell.frame = CGRectMake(3, 14, 100, 100);

} completion:^(BOOL finished) {

    //whatever you want to do upon completion

}];

didselectItemAtIndexPath在你的方法中按照这些思路玩一些东西。

于 2012-12-08T18:03:19.730 回答
21

这是我找到的最简单的解决方案 - 就像一个魅力。流畅的动画,并且不会弄乱布局。

迅速

    collectionView.performBatchUpdates({}){}

对象-C

    [collectionView performBatchUpdates:^{ } completion:^(BOOL finished) { }];

块(Swift 中的闭包)应该故意留空!

于 2016-03-14T19:18:52.283 回答
12

为单元格大小设置动画的一个好方法是在 collectionViewCell 本身中覆盖 isHighlighted。

class CollectionViewCell: UICollectionViewCell {

    override var isHighlighted: Bool {
        didSet {
            if isHighlighted {
                UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
                    self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
                }, completion: nil)
            } else {
                UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
                    self.transform = CGAffineTransform(scaleX: 1, y: 1)
                }, completion: nil)
            }
        }
    }

}
于 2017-09-05T13:24:45.350 回答
10

使用 performBatchUpdates 不会为单元格内容的布局设置动画。相反,您可以使用以下内容:

    collectionView.collectionViewLayout.invalidateLayout()

    UIView.animate(
        withDuration: 0.4,
        delay: 0.0,
        usingSpringWithDamping: 1.0,
        initialSpringVelocity: 0.0,
        options: UIViewAnimationOptions(),
        animations: {
            self.collectionView.layoutIfNeeded()
        },
        completion: nil
    )

这给出了一个非常流畅的动画。

这类似于 Ignatius Tremor 的回答,但过渡动画对我来说无法正常工作。

完整的解决方案参见https://github.com/cnoon/CollectionViewAnimations

于 2017-12-14T20:38:28.200 回答
2

这对我有用。

    collectionView.performBatchUpdates({ () -> Void in
        let ctx = UICollectionViewFlowLayoutInvalidationContext()
        ctx.invalidateFlowLayoutDelegateMetrics = true
        self.collectionView!.collectionViewLayout.invalidateLayoutWithContext(ctx)
    }) { (_: Bool) -> Void in
    }
于 2015-12-02T13:36:16.780 回答
0
class CollectionViewCell: UICollectionViewCell {
   override var isHighlighted: Bool {
       if isHighlighted {
            UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut, animations: {
                self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
            }) { (bool) in
                UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut, animations: {
                    self.transform = CGAffineTransform(scaleX: 1, y: 1)
                }, completion: nil)
            }
        }
   }
}

只需检查 isHighlighted 变量是否更改为 true,然后在完成处理程序中使用 UIView 动画和动画。

于 2019-12-30T20:26:06.290 回答
0

信不信由你

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPat`h: IndexPath) {
            
            // Make your item selected in your data source here
                    
             UIView.animate(withDuration: 0.5) {
            // call reload cells in the animation block and it'll update the selected cell's height with an animation 
                        collectionView.reloadItems(at: [indexPath])
                    }
    }

顺便说一句,我正在使用自动单元格高度,即(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

于 2021-11-09T22:11:52.517 回答