web-dev-qa-db-ja.com

UICollectionView:選択時にセルサイズの変更をアニメートする

私がやりたいのは、セルが選択されたときにUICollectionViewCellのサイズを変更し、その変更をアニメーション化することです。私はすでに、collectionView: didSelectItemAtIndexPath:その後、UICollectionViewでreloadDataを呼び出して、選択したセルを異なるサイズで表示します。

それにもかかわらず、これは一度にすべて発生し、サイズの変化をアニメーション化する方法がわかりません。何か案は?

私はすでに 選択時のuicollectionviewセルのアニメーション化 を見つけましたが、答えは私にとって不特定であり、それが私の場合にも役立つかどうかはまだわかりませんでした。

62
Ignatius Tremor
[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メソッド内のそれらの行に沿って何かで遊んでください。

23
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. 最も重要なステップの1つは、使用しているUICollectionView.collectionViewLayoutinvalidateLayout呼び出しです。忘れてしまうと、コレクションの境界を越えて細胞が成長する可能性が高くなります-これはおそらく起こりたくないことです。また、コードはある時点でレイアウトのセルの新しいサイズを表示する必要があることも考慮してください。私の場合(UICollectionViewFlowLayoutを使用しています)、collectionView:layout:sizeForItemAtIndexPathメソッドを変更して、変更されたセルの新しいサイズを反映させました。その部分を忘れて、最初にinvalidateLayoutを呼び出してからサイズの変化をアニメーション化しようとしても、セルサイズはまったく変化しません。

  2. 最も奇妙な(少なくとも私には)ですが、重要なのは、アニメーションブロック内でinvalidateLayoutnotを呼び出すことです。これを行うと、アニメーションは滑らかではなく、グリッチとフリッカーを表示しません。

  3. コレクションビュー全体ではなく、UIViewの引数としてセルを使用してtransitionWithViews transitionWithView:duration:options:animations:completion:メソッドを呼び出す必要があります。これは、コレクション全体ではなく、アニメーション化するセルだからです。

  4. セルのintrinsicContentSizeメソッドを使用して必要なサイズを返しました。この場合、セルの選択状態に応じて、セルの幅を拡大または縮小してボタンを表示または非表示にします。

これが一部の人々に役立つことを願っています。私にとっては、そのアニメーションを正しく動作させる方法を見つけるのに数時間かかったので、他の人をその時間のかかる負担から解放しようとします。

87
Ignatius Tremor

セルのサイズのアニメーション化は、テーブルビューの場合と同じようにコレクションビューでも機能します。セルのサイズをアニメーション化する場合は、セルの状態を反映するデータモデルを使用し(私の場合はフラグ(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) {

    }];
}
46
Andy Poes

-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];
31
jonsibley

これは私が見つけた最も簡単な解決策です-魅力のように動作します。アニメーションをスムーズにし、レイアウトを台無しにしません。

Swift

    collectionView.performBatchUpdates({}){}

Obj-C

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

ブロック(Swiftのクロージャー)は意図的に空にしておく必要があります!

16
Pavel Gurov

セルサイズをアニメーション化する良い方法は、uicollectionViewCell自体でisSelectedをオーバーライドすることです。

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)
            }
        }
    }

}
9
Cristian Pena

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 を参照してください。

5
phatmann

これは私のために働いた。

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