web-dev-qa-db-fra.com

UICollectionView: Animer le changement de taille de cellule lors de la sélection

Ce que je veux faire, c'est changer la taille d'un UICollectionViewCell et animer ce changement lorsque la cellule est sélectionnée. J'ai déjà réussi à le faire à l'unanimité en marquant une cellule comme sélectionné dans collectionView: didSelectItemAtIndexPath: puis appelez reloadData sur mon UICollectionView, en affichant la cellule sélectionnée avec une taille différente.

Néanmoins, cela se produit tout à la fois et je ne sais pas comment obtenir ce changement de taille animé. Des idées?

J'ai déjà trouvé animer les cellules uicollectionview sur la sélection , mais la réponse était trop peu précise pour moi et je ne savais pas encore si cela pourrait aussi m'aider dans mon cas.

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

}];

Jouez avec quelque chose de ce genre dans votre méthode didselectItemAtIndexPath.

23
Chase Roberts

Avec l'aide de Chase Roberts, j'ai maintenant trouvé la solution à mon problème. Mon code ressemble en gros à ceci:

// 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];

Pour plus d'explications:

  1. L’une des étapes les plus importantes est l’appel invalidateLayout sur le UICollectionView.collectionViewLayout vous utilisez. Si vous l'oubliez, il est probable que des cellules se développent au-delà des limites de votre collection - c'est quelque chose que vous ne voulez probablement pas voir se produire. Considérez également que votre code doit à un moment donné présenter la nouvelle taille de votre cellule à votre mise en page. Dans mon cas (j'utilise un UICollectionViewFlowLayout), j'ai ajusté le collectionView:layout:sizeForItemAtIndexPath méthode pour refléter la nouvelle taille de la cellule modifiée. Si vous oubliez cette partie, la taille de votre cellule n’arrivera à rien si vous appelez d’abord invalidateLayout puis essayez d’animer le changement de taille.

  2. Le plus étrange (du moins pour moi) mais important est que vous appeliez invalidateLayoutnot à l'intérieur du bloc d'animation. Si vous le faites, l’animation ne sera pas fluide, mais scintillante et scintillante.

  3. Vous devez appeler UIViews transitionWithView:duration:options:animations:completion: Méthode avec la cellule comme argument pour transitionWithView, pas la vue de la collection complète, car c'est la cellule que vous souhaitez animer, pas la collection entière.

  4. J'ai utilisé la méthode intrinsicContentSize de ma cellule pour retourner la taille souhaitée. Dans mon cas, je grossis et réduisais la largeur de la cellule pour afficher un bouton ou le masquer, en fonction de l'état de sélection de la cellule. .

J'espère que cela aidera certaines personnes. Pour moi, il a fallu plusieurs heures pour comprendre comment faire fonctionner correctement cette animation. J'essaie donc de libérer les autres de ce fardeau qui prend beaucoup de temps.

87
Ignatius Tremor

L'animation de la taille d'une cellule fonctionne de la même manière pour les vues de collection que pour les vues de tableau. Si vous souhaitez animer la taille d'une cellule, utilisez un modèle de données qui reflète l'état de la cellule (dans mon cas, j'utilise simplement un indicateur (CollectionViewCellExpanded, CollectionViewCellCollapsed) et retourne un CGSize en conséquence.

Lorsque vous appelez et videz l'appel performBathUpdates, la vue s'anime automatiquement.

- (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

J'ai réussi à animer des modifications en utilisant -setCollectionViewLayout:animated: pour créer une nouvelle instance de la même présentation de vue de collection.

Par exemple, si vous utilisez un UICollectionViewFlowLayout, alors:

// 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

C'est la solution la plus simple que j'ai trouvée - fonctionne à merveille. Animation fluide, et ne gâche pas la mise en page.

Swift

    collectionView.performBatchUpdates({}){}

Obj-C

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

Les blocs (fermetures à Swift) doivent être laissés volontairement vides!

16
Pavel Gurov

Un bon moyen d'animer la taille de la cellule est de remplacer isSelected dans la collectionViewCell elle-même.

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

Utiliser performBatchUpdates n'animera pas la présentation du contenu de la cellule. Au lieu de cela, vous pouvez utiliser les éléments suivants:

    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
    )

Cela donne une animation très fluide.

Ceci est similaire à la réponse d'Ignatius Tremor, mais l'animation de transition ne fonctionnait pas correctement pour moi.

Voir https://github.com/cnoon/CollectionViewAnimations pour une solution complète.

5
phatmann

Cela a fonctionné pour moi.

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