Comment définissez-vous la durée des animations UICollectionView?

J'ai une disposition de stream personnalisée qui ajuste les attributes pour les cellules lorsqu'elles sont insérées et supprimées de CollectionView avec les deux fonctions suivantes, mais je n'arrive pas à comprendre comment ajuster la durée d'animation par défaut.

- (UICollectionViewLayoutAtsortingbutes *)initialLayoutAtsortingbutesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { UICollectionViewLayoutAtsortingbutes* atsortingbutes = [self layoutAtsortingbutesForItemAtIndexPath:itemIndexPath]; // Assign the new layout atsortingbutes atsortingbutes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5); atsortingbutes.alpha = 0; return atsortingbutes; 

}

 -(UICollectionViewLayoutAtsortingbutes *)finalLayoutAtsortingbutesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ UICollectionViewLayoutAtsortingbutes* atsortingbutes = [self layoutAtsortingbutesForItemAtIndexPath:itemIndexPath]; // Assign the new layout atsortingbutes atsortingbutes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5); atsortingbutes.alpha = 0; return atsortingbutes; 

}

Pour résoudre le problème sans hack proposé dans la réponse de gavrix, vous pouvez sous-classr UICollectionViewLayoutAtsortingbutes avec la nouvelle propriété CABasicAnimation *transformAnimation , puis créer une transformation personnalisée avec une durée appropriée et l'affecter aux attributes dans initialLayoutAtsortingbutesForAppearingItemAtIndexPath , puis dans UICollectionViewCell appliquer les attributes selon les besoins:

 @interface AnimationCollectionViewLayoutAtsortingbutes : UICollectionViewLayoutAtsortingbutes @property (nonatomic, strong) CABasicAnimation *transformAnimation; @end @implementation AnimationCollectionViewLayoutAtsortingbutes - (id)copyWithZone:(NSZone *)zone { AnimationCollectionViewLayoutAtsortingbutes *atsortingbutes = [super copyWithZone:zone]; atsortingbutes.transformAnimation = _transformAnimation; return atsortingbutes; } - (BOOL)isEqual:(id)other { if (other == self) { return YES; } if (!other || ![[other class] isEqual:[self class]]) { return NO; } if ([(( AnimationCollectionViewLayoutAtsortingbutes *) other) transformAnimation] != [self transformAnimation]) { return NO; } return YES; } @end 

Dans la class Layout

 - (UICollectionViewLayoutAtsortingbutes *)initialLayoutAtsortingbutesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { AnimationCollectionViewLayoutAtsortingbutes* atsortingbutes = (AnimationCollectionViewLayoutAtsortingbutes* )[super initialLayoutAtsortingbutesForAppearingItemAtIndexPath:itemIndexPath]; CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; transformAnimation.duration = 1.0f; CGFloat height = [self collectionViewContentSize].height; transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, 2*height, height)]; transformAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, atsortingbutes.bounds.origin.y, 0)]; transformAnimation.removedOnCompletion = NO; transformAnimation.fillMode = kCAFillModeForwards; atsortingbutes.transformAnimation = transformAnimation; return atsortingbutes; } 

puis dans UICollectionViewCell appliquer les attributes

 - (void) applyLayoutAtsortingbutes:(AnimationCollectionViewLayoutAtsortingbutes *)layoutAtsortingbutes { [[self layer] addAnimation:layoutAtsortingbutes.transformAnimation forKey:@"transform"]; } 

changer la vitesse de CALayer

 @implementation Cell - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.layer.speed =0.2;//default speed is 1 } return self; } 

Sur la base de la réponse de @ rotava, vous pouvez définir temporairement la vitesse de l'animation en utilisant une mise à jour par lots de la vue de collection:

 [self.collectionView performBatchUpdates:^{ [self.collectionView.viewForBaselineLayout.layer setSpeed:0.2]; [self.collectionView insertItemsAtIndexPaths: insertedIndexPaths]; } completion:^(BOOL finished) { [self.collectionView.viewForBaselineLayout.layer setSpeed:1]; }]; 

Après avoir essayé [CATransaction setAnimationDuration:] et [UIView setAnimationDuration:] dans toutes les phases possibles du process de layout sans succès, j'ai trouvé une façon un peu hachée de changer la durée des animations créées par UICollectionView qui ne repose pas sur des API privées.

Vous pouvez utiliser la propriété speed CALayer pour modifier la synchronisation des médias relative des animations effectuées sur une couche donnée. Pour que cela fonctionne avec UICollectionView , vous pouvez changer layer.speed à quelque chose de less de 1 sur le calque de la cellule. Évidemment, ce n'est pas génial que la couche de la cellule ait TOUJOURS une vitesse d'animation non-unité, donc une option est d'envoyer une NSNotification lors de la préparation des animations cellulaires auxquelles vos cellules s'abonnent, cela changera la vitesse du calque, puis le changera à un moment approprié après que les animations sont terminées.

Je ne recommand pas d'utiliser cette approche comme une solution à long terme car c'est plutôt rond, mais ça marche. Espérons qu'Apple exposera plus d'options pour les animations UICollectionView à l'avenir.

UICollectionView initie toutes les animations en interne en utilisant une valeur codée en dur. Cependant, vous pouvez toujours replace cette valeur jusqu'à ce que les animations soient validées. En général, le process ressemble à ceci:

  • commencer les animations
  • aller chercher tous les attributes de layout
  • appliquer des attributes aux vues (UICollectionViewCell)
  • engager des animations

appliquer des attributes est fait sous chaque UICollectionViewCell et vous pouvez replace animationDuration dans la méthode appropriée. Le problème est que UICollectionViewCell a une méthode publique applyLayoutAtsortingbutes: MAIS son implémentation par défaut est vide !. Fondamentalement, UICollectionViewCell a une autre méthode privée appelée _setLayoutAtsortingbutes: et cette méthode privée est appelée par UICollectionView et cette méthode privée appelle applyLayoutAtsortingbutes: à la fin. Les attributes de disposition par défaut, tels que frame, position, transform, sont appliqués avec animationDuration avant que applyLayoutAtsortingbutes: soit appelé. Cela dit, vous devez surcharger animationDuration dans la méthode privée _setLayoutAtsortingbutes:

 - (void) _setLayoutAtsortingbutes:(PSTCollectionViewLayoutAtsortingbutes *)layoutAtsortingbutes { [UIView setAnimationDuration:3.0]; [super _setLayoutAtsortingbutes:layoutAtsortingbutes]; } 

Ceci est évidemment, pas applestore-safe. Vous pouvez utiliser l'un de ces hacks d'exécution pour replace cette méthode privée en toute security.

Vous pouvez définir la propriété de vitesse du calque (comme dans [Réponse de Rotoava]) ( https://stackoverflow.com/a/23146861/5271393 ) pour changer le contrôle de la vitesse de l'animation. Le problème est que vous utilisez des valeurs arbitraires car vous ne connaissez pas la durée réelle de l'animation d'insertion.

En utilisant ce post, vous pouvez déterminer quelle est la durée d'animation par défaut.

 newAnimationDuration = (1/layer.speed)*originalAnimationDuration layer.speed = originalAnimationDuration/newAnimationDuration 

Si vous vouliez faire 400ms de l'animation, vous pourriez:

 - (UICollectionViewLayoutAtsortingbutes *)initialLayoutAtsortingbutesForAppearingItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAtsortingbutes* atsortingbutes = [super finalLayoutAtsortingbutesForDisappearingItemAtIndexPath:indexPath]; //set atsortingbutes here UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; CGFloat originalAnimationDuration = [CATransaction animationDuration]; CGFloat newAnimationDuration = 0.4f; cell.layer.speed = originalAnimationDuration/newAnimationDuration; return atsortingbutes; } 

Dans mon cas, j'avais des cellules qui pouvaient être déplacées hors de l'écran et je voulais changer la durée de l'animation de suppression en fonction de la vitesse du mouvement de panoramique.

Dans la reconnaissance de gestes (qui devrait faire partie de votre vue de collection):

 - (void)handlePanGesture:(UIPanGestureRecognizer *)sender { CGPoint dragVelocityVector = [sender velocityInView:self.collectionView]; CGFloat dragVelocity = sqrt(dragVelocityVector.x*dragVelocityVector.x + dragVelocityVector.y*dragVelocityVector.y); switch (sender.state) { ... case UIGestureRecognizerStateChanged:{ CustomLayoutClass *layout = (CustomLayoutClass *)self.collectionViewLayout; layout.dragSpeed = fabs(dragVelocity); ... } ... } 

Puis dans votre customLayout:

 - (UICollectionViewLayoutAtsortingbutes *)finalLayoutAtsortingbutesForDisappearingItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAtsortingbutes* atsortingbutes = [super finalLayoutAtsortingbutesForDisappearingItemAtIndexPath:indexPath]; CGFloat animationDistance = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); CGFloat originalAnimationDuration = [CATransaction animationDuration]; CGFloat newAnimationDuration = animationDistance/self.dragSpeed; UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; cell.layer.speed = originalAnimationDuration/newAnimationDuration; return atsortingbutes; } 

Vous pouvez modifier la propriété UICollectionView layout.speed, qui devrait changer la durée de l'animation de votre layout …

Une mise à jour de @AshleyMills depuis forBaselineLayout est obsolète

Cela marche

 self.collectionView.performBatchUpdates({ () -> Void in let indexSet = IndexSet(0...(numberOfSections - 1)) self.collectionView.insertSections(indexSet) self.collectionView.forFirstBaselineLayout.layer.speed = 0.5 } , completion: { (finished) -> Void in self.collectionView.forFirstBaselineLayout.layer.speed = 1.0 })