web-dev-qa-db-ja.com

UICollectionViewアニメーションの継続時間をどのように設定しますか?

次の2つの関数を使用してCollectionViewにセルを挿入および削除するときにセルの属性を調整するカスタムフローレイアウトがありますが、デフォルトのアニメーション期間を調整する方法を理解できません。

- (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;
}
37
James Parker

answer by gavrix で提案されたハックなしで問題を解決するには、適切な期間のカスタム変換を作成してinitialLayoutAttributesForAppearingItemAtIndexPathの属性に割り当てるよりも、新しいプロパティCABasicAnimation *transformAnimationでUICollectionViewLayoutAttributesをサブクラス化します。次に、UICollectionViewCellで必要に応じて属性を適用します。

@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

レイアウトクラス

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

次にUICollectionViewCellで属性を適用します

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

cALayerの速度を変更する

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

@rotavaの答えに基づいて、コレクションビューのバッチ更新を使用して、アニメーションの速度を一時的に設定できます。

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

しようとした後[CATransaction setAnimationDuration:]および[UIView setAnimationDuration:]レイアウトプロセスのあらゆるフェーズで成功することなく、プライベートAPIに依存しないUICollectionViewによって作成されたセルアニメーションの継続時間を変更するためのややハックな方法を見つけました。

CALayerspeedプロパティを使用して、特定のレイヤーで実行されるアニメーションの相対的なメディアタイミングを変更できます。これをUICollectionViewと連携させるには、layer.speedセルのレイヤーで1未満の値に。明らかに、セルのレイヤーに常に非ユニティアニメーションの速度を持たせるのはよくありません。そのため、セルアニメーションを準備するときにNSNotificationをディスパッチして、セルがサブスクライブし、レイヤーの速度を変更します。アニメーションが終了した後、適切なタイミングで元に戻します。

このアプローチはかなり遠回りなので、長期的なソリューションとしてこのアプローチを使用することはお勧めしませんが、機能します。うまくいけば、Appleは将来的にUICollectionViewアニメーションのためのより多くのオプションを公開するでしょう。

5
roperklacks

UICollectionViewは、ハードコードされた値を使用して、すべてのアニメーションを内部で開始します。ただし、アニメーションがコミットされるまで、いつでもその値をオーバーライドできます。一般に、プロセスは次のようになります。

  • アニメーションを開始
  • すべてのレイアウト属性をフェッチします
  • ビューに属性を適用する(UICollectionViewCell's)
  • アニメーションをコミットする

属性の適用は各UICollectionViewCellで行われ、適切なメソッドでanimationDurationをオーバーライドできます。問題は、UICollectionViewCellにパブリックメソッドapplyLayoutAttributes:があることですが、デフォルトの実装は空です!基本的に、UICollectionViewCellには_setLayoutAttributes:と呼ばれる他のプライベートメソッドがあり、このプライベートメソッドはUICollectionViewによって呼び出され、このプライベートメソッドは最後にapplyLayoutAttributes:を呼び出します。フレーム、位置、変換などのデフォルトのレイアウト属性は、applyLayoutAttributes:が呼び出される前に、現在のanimationDurationで適用されます。つまり、プライベートメソッド_setLayoutAttributes:animationDurationをオーバーライドする必要があります

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

これは明らかに、アップルストアセーフではありません。これらのランタイムハックの1つを使用して、このプライベートメソッドを安全にオーバーライドできます。

4
gavrix

レイヤーの速度プロパティを設定して(たとえば Rotoava's Answer のように)、アニメーションの速度を変更できます。問題は、挿入アニメーションの実際の継続時間がわからないため、任意の値を使用していることです。

この投稿 を使用すると、デフォルトのアニメーション期間がわかります。

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

アニメーションを400ms長くしたい場合は、レイアウトで次のようにします。

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

私の場合、画面外にドラッグできるセルがあり、パンジェスチャの速度に基づいて削除アニメーションの期間を変更したいと思いました。

ジェスチャーレコグナイザー(コレクションビューの一部である必要があります)で:

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

次に、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

ForBaselineLayoutが廃止されたための@AshleyMillsの更新

これは機能します

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

サブクラス化なし:

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

UICollectionView layout.speedプロパティを変更できます。これにより、レイアウトのアニメーション期間が変更されます...

0
Skodik.o