2012-10-16 7 views
30

Ich habe ein benutzerdefiniertes Ablauflayout, das die Attribute für Zellen beim Einfügen und Löschen aus der CollectionView mit den folgenden beiden Funktionen anpasst, aber ich kann nicht herausfinden, wie Sie die Standardanimationsdauer anpassen würden.Wie legen Sie die Dauer für UICollectionView-Animationen fest?

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

}

+0

der Dokumentation von Apple Laut „Wenn Layoutänderungen animieren, werden die Animation Timing und Parameter, die von der Sammlung Ansicht gesteuert.“ Dies bezieht sich auf die setCollectionView: animated: -Methode, aber ich vermute, dass dies auch für das Ändern der Grenzen der Sammlungsansicht gilt. Sorry, ich kann nicht mehr Hilfe sein, ich stecke auf dem gleichen Problem fest. Ich vermute, dass die Antwort irgendwo innerhalb des UICollectionView-Objekts selbst liegt. – Ash

Antwort

22

Um Problem ohne Hack zu lösen, die in der answer by gavrix Sie UICollectionViewLayoutAttributes mit neuen Eigenschaftsunterklasse könnte CABasicAnimation *transformAnimation, vorgeschlagen wurde als individuelle Transformation mit einem geeigneten Dauer erstellen und auf Attribute in initialLayoutAttributesForAppearingItemAtIndexPath zuweisen, dann in UICollectionViewCell die Attribute gelten je nach Bedarf:

@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 

im Layout-Klasse

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

dann in UICollectionViewCell gelten die Attribute

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

Sie müssen auch '+ layoutAttributesClass' überschreiben, um' [AnimationCollectionViewLayoutAttributes class] 'in der Layoutklasse zurückzugeben. –

+1

'+ (Klasse) layoutAttributesClass { Rückgabe [AnimationCollectionViewLayoutAttributes-Klasse]; } ' –

+1

fügen Sie obige Methode in Ihrer CustomFlowLayout.m-Klasse hinzu! –

3

UICollectionView initiiert alle intern Animationen etwas fest codierten Wert verwenden. Sie können diesen Wert jedoch immer überschreiben, bis die Animationen festgeschrieben sind. Im Allgemeinen sieht Prozess wie folgt aus:

  • Animationen
  • anwenden Attribute Ansichten (UICollectionViewCell der)
  • Animationen begehen

Anwendung Attribute unter wird

  • holen alle Layout attribues beginnen getan Jede UICollectionViewCell und Sie können animationDuration in geeigneter Methode überschreiben. Das Problem ist, dass UICollectionViewCell die öffentliche Methode applyLayoutAttributes: hat, aber die Standardimplementierung ist leer !. Grundsätzlich hat UICollectionViewCell andere private Methode namens _setLayoutAttributes: und diese private Methode wird von UICollectionView aufgerufen und diese private Methode ruft applyLayoutAttributes: am Ende. Standardlayoutattribute wie Frame, Position, Transformation werden mit dem aktuellen animationDuration übernommen, bevor applyLayoutAttributes: aufgerufen wird. Das heißt, Sie haben animationDuration in private Methode _setLayoutAttributes:

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

    Dies ist offensichtlich nicht Applessicher außer Kraft zu setzen. Sie können einen dieser Laufzeit-Hacks verwenden, um diese private Methode sicher zu überschreiben.

  • 4

    Nach [CATransaction setAnimationDuration:] und [UIView setAnimationDuration:] in jeder möglichen Phase des Layout-Prozesses ohne Erfolg versucht, dachte ich, einen etwas hacky Weg durch UICollectionView erstellt die Dauer der Zelle Animationen zu ändern, die auf private API beruht nicht.

    Sie können die CALayer Eigenschaft speed verwenden, um das relative Medientiming von Animationen zu ändern, die auf einer bestimmten Ebene ausgeführt werden. Um mit UICollectionView zu arbeiten, können Sie layer.speed auf etwas weniger als 1 auf der Ebene der Zelle ändern. Offensichtlich ist es nicht toll, wenn die Schicht der Zelle IMMER eine Animationsgeschwindigkeit ungleich der Einheit hat. Eine Möglichkeit besteht also darin, eine bei der Vorbereitung für Zellanimationen zu versenden, die die Zellen subskribieren, die Schichtgeschwindigkeit ändern und dann zurück ändern zu einem geeigneten Zeitpunkt, nachdem die Animationen beendet sind.

    Ich empfehle diesen Ansatz nicht als eine langfristige Lösung, da es ziemlich umständlich ist, aber es funktioniert. Hoffentlich wird Apple in Zukunft weitere Optionen für UICollectionView-Animationen verfügbar machen.

    16

    Änderung CALayer die Geschwindigkeit

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

    Das funktioniert perfekt, froh zu sagen. Es ist viel einfacher als die anderen Workarounds. Ich füge hinzu, dass eine höhere Zahl schnellere Animationen bedeutet. – sudo

    0

    Sie können UICollectionView Eigenschaft layout.speed ändern, sollte das Animation Dauer Ihres Layout ändern ...

    3

    Sie die Geschwindigkeit Eigenschaft der Ebene festlegen (wie in [Rotoavas Antwort]) (https://stackoverflow.com/a/23146861/5271393), um die Geschwindigkeit der Animation zu ändern. Das Problem besteht darin, dass Sie willkürliche Werte verwenden, da Sie die tatsächliche Dauer der Einfügungsanimation nicht kennen.

    Mit this post können Sie herausfinden, was die Standardanimationsdauer ist.

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

    Wenn Sie die Animation 400ms lang, in Ihrem Layout würden Sie machen wollte:

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

    In meinem Fall hatte ich Zellen, die gezogen werden könnte außerhalb des Bildschirms, und ich wollte ändern, um die Dauer der die Löschanimation basierend auf der Geschwindigkeit der Schwenkgeste.

    Im Gestenerkenner (der Teil Ihrer Sammlung Ansicht sein soll):

    - (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); 
        ... 
        } 
        ... 
    } 
    

    Dann in Ihrem 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; 
    } 
    
    8

    Aufbauend auf @ Rotava Antwort, Sie vorübergehend die Animation einstellen Geschwindigkeit durch Verwendung einer Stapelaktualisierung der Sammlungsansicht:

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

    Ich frage mich, ob der boolesche "fertig" hier wichtig ist. Bei einigen Aufrufen (ich erinnere mich nicht genau an welche), wird der 'Completion'-Block mehr als einmal aufgerufen. Um ganz sicher zu sein, dass die Animationen beendet sind, würde ich 'if (finished) {/ * ... * /}' machen. Warum ist das nicht nötig? Oder ist es und du hast es einfach übersprungen? –

    0

    Ein Update für @Ashle yMills seit forBaselineLayout ist

    veraltet

    Dies funktioniert

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