【问题标题】:UICollectionView: Animate cell size change on selectionUICollectionView:选择时动画单元格大小变化
【发布时间】:2012-11-26 15:19:48
【问题描述】:

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

尽管如此,这一切都是同时发生的,我不知道如何让大小的变化变成动画。有什么想法吗?

我已经找到了Animate uicollectionview cells on selection,但答案对我来说是不确定的,我还不知道它是否也可以帮助我。

【问题讨论】:

    标签: ios swift uicollectionview


    【解决方案1】:

    信不信由你

    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

    【讨论】:

      【解决方案2】:

      设置单元格大小动画的一个好方法是在 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)
                  }
              }
          }
      
      }
      

      【讨论】:

      • 您说“设置单元格大小动画的一个好方法是在 collectionViewCell 本身中覆盖 isSelected。” ,但你覆盖isHighlighted。这是两种不同的状态。
      【解决方案3】:
      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 动画和动画。

      【讨论】:

        【解决方案4】:
        [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 方法中玩弄一些类似的东西。

        【讨论】:

        • 玩弄它似乎已经解决了我的问题。我是对的,没有根据您的建议改进我的解决方案,以便在我完成后立即发布它。到目前为止非常感谢。
        • 它似乎有效,但结果在视觉上很糟糕。它似乎结结巴巴地停了下来。有什么想法吗?
        【解决方案5】:

        使用 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 以获得完整的解决方案。

        【讨论】:

        • 赞成这个答案,但它仍然不完美。动画在重新绘制之前拉伸单元格。但实际上动画开始变得很好,因为单元格的高度也是动画的。
        【解决方案6】:

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

        斯威夫特

            collectionView.performBatchUpdates({}){}
        

        Obj-C

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

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

        【讨论】:

        • 我已经在更新块中使用reloadDatamoveItemAtIndexPath: toIndexPath:reloadItemsAtIndexPaths: 尝试了这个解决方案,然后意识到该块应该留空。现在真的像一个魅力。
        【解决方案7】:

        这对我有用。

            collectionView.performBatchUpdates({ () -> Void in
                let ctx = UICollectionViewFlowLayoutInvalidationContext()
                ctx.invalidateFlowLayoutDelegateMetrics = true
                self.collectionView!.collectionViewLayout.invalidateLayoutWithContext(ctx)
            }) { (_: Bool) -> Void in
            }
        

        【讨论】:

        • performBatchUpdates 的内部不是必需的,看我的回答
        【解决方案8】:

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

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

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

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

        【讨论】:

        • 发布后续工作的好工作。
        • 关于避免保留循环的部分,不应该被声明为 outside 块吗?由于您使用的是 uicollectionview(iOS 6+),因此首选限定符现在是 __weak。如果cell 出现在您的块中,则会创建一个强引用,因此您不会避免任何保留周期。不过,非常感谢您的跟进,非常有帮助!
        • 嘿@matehat,我现在更改了代码以反映您的注释。很高兴听到你喜欢这个答案:)
        • 这里我只想补充一点,你不需要使用__weak。当您的块保留一些本身保留该块的对象时,通常会发生保留循环;即去 self.block = ^{self.someInteger++;}。由于您的单元格没有保留块,因此没有保留周期:)
        • 对阅读本文的人的一点建议:在单元格中使用 AutoLayout 可能会破坏这一点。我不得不更改单元格的高度约束,然后在动画块内使用 [cell layoutIfNeeded]
        【解决方案9】:

        通过使用-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];
        

        【讨论】:

        • 这提供了迄今为止最流畅的动画。很好的解决方案。
        • 其他答案都不适合我——其他人在单元格上执行了动画,但是使布局无效的行为(无论是明确的还是在幕后)也导致了其他人的位置问题单个单元格动画之前或之后的单元格。这是唯一对我有用的答案!
        • 除了在外壳上有效(在iPhone4S,iOS8上测试):在动画期间,如果滚动CollectionView,单元格将被隐藏。
        • 缺点是contentOffset 发生了变化。
        【解决方案10】:

        对单元格大小进行动画处理对于集合视图和表格视图的工作方式相同。如果您想为单元格的大小设置动画,请拥有一个反映单元格状态的数据模型(在我的例子中,我只使用一个标志 (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) {
        
            }];
        }
        

        【讨论】:

        • 很好的答案。就我而言,我只需要更改模型,然后调用 performBatchUpdates 并在块内调用 invalidateLayout。
        • 我有一个自定义布局,它包含一个关于哪个索引路径是“突出”索引路径的瞬态属性,当我设置该索引路径时,视图会重绘自身并将项目移动到指向集合顶部的索引路径,远离其余项目(有点像 Passbook)。设置该属性并使用建议的@DorianRoy 是一个巨大的帮助。
        • 这是一个更好的解决方案,特别是考虑到在单元重用后更改仍然存在。
        • 为我工作,使用 Swift 4.2
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-08
        • 2018-08-22
        • 1970-01-01
        • 1970-01-01
        • 2022-01-02
        • 1970-01-01
        相关资源
        最近更新 更多