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;
}
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"];
}
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;
}
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];
}];
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.
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:
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é.
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;
}
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
})
Sans sous-classement:
[UIView animateWithDuration:2.0 animations:^{
[self.collection reloadSections:indexSet];
}];
Vous pouvez modifier la propriété UICollectionView layout.speed, qui devrait modifier la durée de l'animation de votre présentation ...