6

キー フレーム アニメーションの別のテストでは、UIImageView (と呼ばれるtheImage) をベジエ パスに沿って移動し、移動に合わせて拡大することを組み合わせて、パスの最後に 2 倍の大きな画像を生成します。これを行うための最初のコードには、アニメーションを開始するための次の要素が含まれています。

UIImageView* theImage = ....
float scaleFactor = 2.0;
....

theImage.center = destination;
theImage.transform = CGAffineTransformMakeScale(1.0,1.0);

CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(theImage.image.size.height*scaleFactor, theImage.image.size.width*scaleFactor)]];
resizeAnimation.fillMode = kCAFillModeBackwards;
resizeAnimation.removedOnCompletion = NO;  

CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.path = [jdPath path].CGPath;
pathAnimation.fillMode = kCAFillModeBackwards;
pathAnimation.removedOnCompletion = NO;

CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:pathAnimation, resizeAnimation, nil];
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.removedOnCompletion = NO;
group.duration = duration;
group.delegate = self;

[theImage.layer addAnimation:group forKey:@"animateImage"];

次に、アニメーションが完了したら、画像をより大きなサイズで保持したいので、次を実装します。

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
   theImage.transform = CGAffineTransformMakeScale(scaleFactor,scaleFactor);
}

これはすべて機能します。問題は、アニメーションの最後にtheImage一瞬ちらつくことです。これは見栄えが悪くなります。これは、変換を新しいサイズに設定したアニメーションの最後のトランジションであると推測しています。

これを実験する際に、上記とは少し異なる形式を試しましたが、それでも同じちらつきが発生しました。

CAKeyframeAnimation *resizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue* startSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)];
NSValue* endSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, scaleFactor, scaleFactor, 1.0)]; 
NSArray* sizeKeys = [NSArray arrayWithObjects:startSizeKey, endSizeKey, nil];
[resizeAnimation setValues:sizeKeys];

....

theImage.transform = CGAffineTransformMakeScale(scaleFactor,scaleFactor);

しかし、オリジナルと同じサイズでアニメーションを終了すると、ちらつきはありませんでした:

CAKeyframeAnimation *resizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue* startSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)];
NSValue* middleSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, scaleFactor, scaleFactor, 1.0)];
NSValue* endSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)]; 
NSArray* sizeKeys = [NSArray arrayWithObjects:startSizeKey, middleSizeKey, endSizeKey, nil];
[resizeAnimation setValues:sizeKeys];

....

theImage.transform = CGAffineTransformMakeScale(1.0,1.0);

だから私の大きな問題は、ちらつきなしでこの画像をアニメーション化し、アニメーションの最後に別のサイズで終わるにはどうすればよいですか?


3月2日編集

私の最初のテストは、画像を拡大することでした。私はちょうどそれを縮小しようとしました (IE scaleFactor = 0.4)、そしてちらつきはもっと目に見えて、私が見ているものに関してもっと明白でした. これは一連のイベントでした:

  1. 元のサイズの画像が画面の開始位置に描画されます。
  2. 画像がパスに沿って移動すると、画像は滑らかに縮小します。
  3. 完全に縮小されたイメージは、パスの最後に到達します。
  4. その後、画像は元のサイズでペイントされます。
  5. イメージは最終的に縮小されたサイズでペイントされます。

したがって、私が見ているちらつきはステップ 4 のようです。


3月22日編集

ベジェ パスに沿ったオブジェクトの移動を示すデモ プロジェクトを GitHub にアップロードしました。コードはPathMoveにあります。

iOS でオブジェクトをベジエ パスに沿って移動するというブログにも書いています。

4

3 に答える 3

8

Core Animation を使用してビューのレイヤーをアニメーション化するのは難しい場合があります。混乱を招くいくつかのことがあります。

  • レイヤーにアニメーションを設定しても、レイヤーのプロパティは変更されません。代わりに、アニメーションが適用されている限り、画面上の元の「モデル レイヤー」を置き換える「プレゼンテーション レイヤー」のプロパティを変更します。

  • レイヤーのプロパティを変更すると、通常、レイヤーに暗黙的なアニメーションが追加され、プロパティ名がアニメーションのキーになります。したがって、プロパティを明示的にアニメーション化する場合は、通常、プロパティを最終的な値に設定してから、キーがプロパティ名であるアニメーションを追加して、暗黙的なアニメーションをオーバーライドします

  • ビューは通常、そのレイヤーで暗黙的なアニメーションを無効にします。また、他のやや不思議な方法でレイヤーのプロパティをいじります。

また、ビューの境界をアニメーション化して拡大し、最後にスケール変換に切り替えるのは混乱を招きます。

やりたいことを実現する最も簡単な方法はUIView、可能な限りアニメーション メソッドを使用し、キーフレーム アニメーションには Core Animation のみを使用することだと思います。UIView独自のアニメーションを追加した後、キーフレーム アニメーションをビューのレイヤーに追加できます。キーフレーム アニメーションは、 によって追加されたアニメーションをオーバーライドしますUIView

これは私のために働いた:

- (IBAction)animate:(id)sender {
    UIImageView* theImage = self.imageView;
    CGFloat scaleFactor = 2;
    NSTimeInterval duration = 1;

    UIBezierPath *path = [self animationPathFromStartingPoint:theImage.center];
    CGPoint destination = [path currentPoint];

    [UIView animateWithDuration:duration animations:^{
        // UIView will add animations for both of these changes.
        theImage.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
        theImage.center = destination;

        // Prepare my own keypath animation for the layer position.
        // The layer position is the same as the view center.
        CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        positionAnimation.path = path.CGPath;

        // Copy properties from UIView's animation.
        CAAnimation *autoAnimation = [theImage.layer animationForKey:@"position"];
        positionAnimation.duration = autoAnimation.duration;
        positionAnimation.fillMode = autoAnimation.fillMode;

        // Replace UIView's animation with my animation.
        [theImage.layer addAnimation:positionAnimation forKey:positionAnimation.keyPath];
    }];
}
于 2012-03-03T20:33:50.127 に答える
4

今週、いくつかのCAAnimationsを試していましたが、アニメーションの最後にちらつきがあることに気づきました。特に、fillColorも変更しながら、円から正方形にアニメーション化します。

各CAAnimationには、removedOnCompletionデフォルトでYESと呼ばれるプロパティがあります。これは、アニメーションが完了するとアニメーションが消え(つまり、トランジション、スケール、回転など)、元のレイヤーが残ることを意味します。

すでにremoveedOnCompletionプロパティをNOに設定しているので、アニメーションの実行を、デリゲートやanimationDidStopの代わりにCATransactionsを使用するようにシフトすることをお勧めします...

[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction setCompletionBlock: ^{ theImage.transform = ...}];

// ... CAAnimation Stuff ... //

[CATransaction commit];

http://zearfoss.wordpress.com/2011/02/24/core-animation-catransaction-protip/のように、アニメーションを作成する前に、トランザクションの完了ブロック呼び出しを行います。

以下は私の方法の1つからのものです:

[CATransaction begin];
CABasicAnimation *animation = ...;
animation.fromValue = ...;
animation.toValue = ...;
[CATransaction setCompletionBlock:^ { self.shadowRadius = _shadowRadius; }];
[self addAnimation:animation forKey:@"animateShadowOpacity"];
[CATransaction commit];

そして、私はこのアニメーションを作成しましたが、最後にグリッチがなくても問題なく動作します。セットアップとトリガーはウィンドウにあるカスタムメソッドであり、マウスダウンでアニメーションをトリガーします。

UIImageView *imgView;
UIBezierPath *animationPath;

-(void)setup {
    canvas = (C4View *)self.view;
    imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"img256.png"]];
    imgView.frame = CGRectMake(0, 0, 128, 128);
    imgView.center = CGPointMake(384, 128);
    [canvas addSubview:imgView];

}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [UIImageView animateWithDuration:2.0f animations:^{
        [CATransaction begin];        
        CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        pathAnimation.duration = 2.0f;
        pathAnimation.calculationMode = kCAAnimationPaced;
        animationPath = [UIBezierPath bezierPath];
        [animationPath moveToPoint:imgView.center];
        [animationPath addLineToPoint:CGPointMake(128, 512)];
        [animationPath addLineToPoint:CGPointMake(384, 896)];
        pathAnimation.path = animationPath.CGPath;
        pathAnimation.fillMode = kCAFillModeForwards;
        pathAnimation.removedOnCompletion = NO;
        [imgView.layer addAnimation:pathAnimation forKey:@"animatePosition"];
        [CATransaction commit];

        CGFloat scaleFactor = 2.0f;
        CGRect newFrame = imgView.frame;
        newFrame.size.width *= scaleFactor;
        newFrame.size.height *= scaleFactor;
        newFrame.origin = CGPointMake(256, 0);
        imgView.frame = newFrame;
        imgView.transform = CGAffineTransformRotate(imgView.transform,90.0*M_PI/180);

    }];
}
于 2012-03-03T07:27:36.777 に答える
4

CAAnimation最終状態がそれ自体が暗黙的なアニメーションを作成するような方法で割り当てられた場合、s は最後にちらつきます。CAAnimationは、遷移を視覚化するためのオブジェクト プロパティの一時的な調整であることに注意してください。アニメーションが終了したとき、レイヤーの状態がまだ元の開始状態である場合、メソッドで行う最終的なレイヤー状態を設定するまで、一時的に表示されますanimationDidStop:

さらに、アニメーションはbounds.sizeレイヤーのプロパティを調整しているため、変換調整を最終状態として使用するのではなく、同様に最終状態を設定する必要があります。transformの代わりに、プロパティをアニメーションのアニメーション プロパティとして使用することもできますbounds.size

これを解決するには、アニメーションを割り当てた直後に、レイヤーの透過状態を目的の最終状態に変更して、アニメーションが完了したときにちらつきがないようにしますが、アニメーションが開始する前に暗黙的なアニメーションをトリガーしないようにします。具体的には、あなたの場合、アニメーション設定の最後にこれを行う必要があります:

UIImageView* theImage = ....
float scaleFactor = 2.0;
....

theImage.center = destination;
theImage.transform = CGAffineTransformMakeScale(1.0,1.0);

CGSize finalSize = CGSizeMake(theImage.image.size.height*scaleFactor, theImage.image.size.width*scaleFactor);
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:finalSize]];
resizeAnimation.fillMode = kCAFillModeBackwards;
resizeAnimation.removedOnCompletion = NO;  

CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.path = [jdPath path].CGPath;
pathAnimation.fillMode = kCAFillModeBackwards;
pathAnimation.removedOnCompletion = NO;

CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:pathAnimation, resizeAnimation, nil];
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.removedOnCompletion = NO;
group.duration = duration;
group.delegate = self;

[theImage.layer addAnimation:group forKey:@"animateImage"];
[CATransaction begin];
[CATransaction setDisableActions:YES];
theImage.bounds = CGRectMake( theImage.bounds.origin.x, theImage.bounds.origin.y, finalSize.width, finalSize.height );
[CATransaction commit];

次に、メソッドの変換調整を削除しますanimationDidStop:

于 2012-03-03T16:45:43.180 に答える