web-dev-qa-db-fra.com

Comment définissez-vous la durée pour UICollectionView Animations?

J'ai une disposition de flux personnalisée qui ajuste les attributs des cellules lorsqu'elles sont insérées et supprimées de CollectionView avec les deux fonctions suivantes, mais je ne parviens pas à comprendre comment vous ajusteriez la durée d'animation par défaut.

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];

    // Assign the new layout attributes
    attributes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5);
    attributes.alpha = 0;

    return attributes;
}

- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {

    UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];

    // Assign the new layout attributes
    attributes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5);
    attributes.alpha = 0;

    return attributes;
}
36
James Parker

Pour résoudre le problème sans piratage proposé dans le réponse par gavrix , Vous pouvez sous-classer UICollectionViewLayoutAttributes avec la nouvelle propriété CABasicAnimation *transformAnimation, puis créer une transformation personnalisée avec une durée appropriée et l'attribuer aux attributs dans initialLayoutAttributesForAppearingItemAtIndexPath, puis dans UICollectionViewCell, appliquez les attributs selon vos besoins:

@interface AnimationCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes
@property (nonatomic, strong)  CABasicAnimation *transformAnimation;
@end

@implementation AnimationCollectionViewLayoutAttributes
- (id)copyWithZone:(NSZone *)zone
{
    AnimationCollectionViewLayoutAttributes *attributes = [super copyWithZone:zone];
    attributes.transformAnimation = _transformAnimation;
    return attributes;
}

- (BOOL)isEqual:(id)other {
    if (other == self) {
        return YES;
    }
    if (!other || ![[other class] isEqual:[self class]]) {
        return NO;
    }
    if ([(( AnimationCollectionViewLayoutAttributes *) other) transformAnimation] != [self transformAnimation]) {
        return NO;
    }

    return YES;
}
@end

En cours de mise en page

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    AnimationCollectionViewLayoutAttributes* attributes = (AnimationCollectionViewLayoutAttributes* )[super initialLayoutAttributesForAppearingItemAtIndexPath: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, attributes.bounds.Origin.y, 0)];
    transformAnimation.removedOnCompletion = NO;
    transformAnimation.fillMode = kCAFillModeForwards;
    attributes.transformAnimation = transformAnimation;
    return attributes;
}

+ (Class)layoutAttributesClass { 
    return [AnimationCollectionViewLayoutAttributes class]; 
}

puis dans UICollectionViewCell, appliquez les attributs

- (void) applyLayoutAttributes:(AnimationCollectionViewLayoutAttributes *)layoutAttributes
{
    [[self layer] addAnimation:layoutAttributes.transformAnimation forKey:@"transform"];
}
26
zyxel

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;
}
17
rotoava

En vous appuyant sur la réponse de @ rotava, vous pouvez définir temporairement la vitesse de l'animation à l'aide d'une mise à jour par lot de la vue de collecte:

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

Après avoir essayé sans succès [CATransaction setAnimationDuration:] et [UIView setAnimationDuration:] dans toutes les phases possibles du processus de mise en page, j'ai trouvé un moyen quelque peu hacky de changer la durée des animations de cellule créées par UICollectionView qui ne reposent pas sur des API privées.

Vous pouvez utiliser la propriété CALayer de speed pour modifier le minutage multimédia relatif des animations effectuées sur un calque donné. Pour que cela fonctionne avec UICollectionView, vous pouvez remplacer layer.speed par un nombre inférieur à 1 sur le calque de la cellule. Évidemment, il n’est pas bon d’avoir TOUJOURS une couche avec une vitesse d’animation différente de celle de la cellule. L’une des options consiste à envoyer une NSNotification lors de la préparation des animations de cellule auxquelles vos cellules souscrivent, ce qui modifiera la vitesse de la couche, puis la modifiera. à un moment approprié après la fin des animations.

Je ne recommande pas d'utiliser cette approche comme solution à long terme, car elle est plutôt détournée, mais cela fonctionne. Espérons que Apple exposera plus d'options pour les animations UICollectionView à l'avenir.

4
roperklacks

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

  • commencer les animations
  • chercher tous les attributs de la mise en page 
  • appliquer des attributs aux vues (UICollectionViewCell's)
  • commettre des animations

l'application d'attributs est effectuée sous chaque UICollectionViewCell et vous pouvez remplacer animationDuration dans la méthode appropriée. Le problème est que UICollectionViewCell a la méthode publique applyLayoutAttributes: BUT, mais que son implémentation par défaut est vide !. Fondamentalement, UICollectionViewCell a une autre méthode privée appelée _setLayoutAttributes: et cette méthode privée est appelée par UICollectionView et cette méthode privée appelle applyLayoutAttributes: à la fin. Les attributs de présentation par défaut, tels que frame, position, transform, sont appliqués avec animationDuration actuel avant que applyLayoutAttributes: soit appelé. Cela dit, vous devez remplacer animationDuration dans la méthode privée _setLayoutAttributes:.

- (void) _setLayoutAttributes:(PSTCollectionViewLayoutAttributes *)layoutAttributes
{
    [UIView setAnimationDuration:3.0];
    [super _setLayoutAttributes:layoutAttributes];
}

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

4
gavrix

Vous pouvez définir la propriété de vitesse du calque (comme dans Réponse de Rotoava) pour modifier 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 cet article vous pouvez déterminer la durée d'animation par défaut.

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

Si vous souhaitez que l'animation dure 400 ms, vous devez dans votre mise en page:

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

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 geste de panoramique.

Dans la reconnaissance de geste (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:

- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes* attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath: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 attributes;
}
3
ndey96

Une mise à jour de @AshleyMills depuis que 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
})
1
Ryan Heitner

Sans sous-classement:

[UIView animateWithDuration:2.0 animations:^{
  [self.collection reloadSections:indexSet];
}];
1
bauerMusic

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

0
Skodik.o