18

アイテムが削除されるユーザー インターフェイスがあり、iOS メールで「フォルダーに移動」効果を模倣したいと考えています。小さな文字のアイコンがフォルダに「放り込まれる」効果。代わりに、私のものはゴミ箱に捨てられます。

CAAnimationレイヤー上のを使って実装してみました。ドキュメントを読むことができる限り、 abyValueと aを設定できるはずでtoValue、CAAnimation は値を補間する必要があります。少しカーブさせたいので、アイテムはアイテムの開始位置の少し上と左側のポイントを通過します。

    CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position"];
[animation setDuration:2.0f];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];    
[animation setTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut]];
[animation setFromValue:[NSValue valueWithCGPoint:fromPoint]];
[animation setByValue:[NSValue valueWithCGPoint:byPoint]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(512.0f, 800.0f)]];
[animation setRepeatCount:1.0];

私はしばらくこれをいじりましたが、Apple は線形補間を意味しているように思えます。byValue を追加しても、適切な円弧または曲線が計算されず、それを介して項目がアニメーション化されません。

このようなアニメーションを行うにはどうすればよいでしょうか。

助けてくれてありがとう。

4

5 に答える 5

32

UIBezierPath の使用

import QuartzCore( iOS 6 以前を使用している場合は、リンクしてから を忘れないでください)

サンプルコード

パスをたどるアニメーションを使用できますが、便利なことに、 から取得できる をCAKeyframeAnimationサポートしています。スイフト3CGPathUIBezierPath

func animate(view : UIView, fromPoint start : CGPoint, toPoint end: CGPoint)
{
    // The animation
    let animation = CAKeyframeAnimation(keyPath: "position")

    // Animation's path
    let path = UIBezierPath()

    // Move the "cursor" to the start
    path.move(to: start)

    // Calculate the control points
    let c1 = CGPoint(x: start.x + 64, y: start.y)
    let c2 = CGPoint(x: end.x,        y: end.y - 128)

    // Draw a curve towards the end, using control points
    path.addCurve(to: end, controlPoint1: c1, controlPoint2: c2)

    // Use this path as the animation's path (casted to CGPath)
    animation.path = path.cgPath;

    // The other animations properties
    animation.fillMode              = kCAFillModeForwards
    animation.isRemovedOnCompletion = false
    animation.duration              = 1.0
    animation.timingFunction        = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseIn)

    // Apply it
    view.layer.add(animation, forKey:"trash")
}

UIBezierPath について

ベジエ パス (正確にはベジエ曲線) は、Photoshop、花火、スケッチで見られるものとまったく同じように機能します。頂点ごとに 1 つずつ、2 つの「コントロール ポイント」があります。たとえば、私が作成したばかりのアニメーション:

ここに画像の説明を入力

そのようなベジエパスを動作させます。詳細についてはドキュメントを参照してください。ただし、基本的には、アークを特定の方向に「引っ張る」2 つのポイントです。

パスを描く

の優れた機能の 1 つUIBezierPathは、 を使用して画面上に描画できることですCAShapeLayer。これにより、それがたどるパスを視覚化するのに役立ちます。

// Drawing the path
let *layer          = CAShapeLayer()
layer.path          = path.cgPath
layer.strokeColor   = UIColor.black.cgColor
layer.lineWidth     = 1.0
layer.fillColor     = nil

self.view.layer.addSublayer(layer)

元の例の改善

独自のベジエ パスを計算するという考え方は、完全に動的にすることができるということです。したがって、アニメーションは、例で行ったようにハードコーディングするだけでなく、複数の要因に基づいて実行する曲線を変更できます。たとえば、コントロール ポイントは次のように計算できます。

// Calculate the control points
let factor : CGFloat = 0.5

let deltaX : CGFloat = end.x - start.x
let deltaY : CGFloat = end.y - start.y

let c1 = CGPoint(x: start.x + deltaX * factor, y: start.y)
let c2 = CGPoint(x: end.x                    , y: end.y - deltaY * factor)

このコードの最後のビットは、ポイントが前の図のようになるようにしますが、ポイントが形成する三角形に関して可変量で、「張力」値に相当する係数を掛けます。

于 2013-07-11T08:31:38.530 に答える
8

CABasicAnimation位置を a でアニメーション化すると、位置が直線になることは間違いありません。CAKeyframeAnimationより高度なアニメーションを行うために呼び出される別のクラスがあります。

の配列values

の代わりにtoValuefromValueおよびbyValue基本的なアニメーションの場合、途中で値を決定するためにの配列valuesまたは完全なのいずれかを使用できます。path位置を最初に横に、次に下にアニメーション化したい場合は、3 つの位置 (開始、中間、終了) の配列を渡すことができます。

CGPoint startPoint = myView.layer.position;
CGPoint endPoint   = CGPointMake(512.0f, 800.0f); // or any point
CGPoint midPoint   = CGPointMake(endPoint.x, startPoint.y);

CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:@"position"];
move.values = @[[NSValue valueWithCGPoint:startPoint],
                [NSValue valueWithCGPoint:midPoint],
                [NSValue valueWithCGPoint:endPoint]];
move.duration = 2.0f;

myView.layer.position = endPoint; // instead of removeOnCompletion
[myView.layer addAnimation:move forKey:@"move the view"];

これを行うと、ビューが開始点から中間点まで直線で移動し、別の直線で終点まで移動することに気付くでしょう。中点を経由して最初から最後まで弧を描くために欠けている部分はcalculationMode、アニメーションの を変更することです。

move.calculationMode = kCAAnimationCubic;

tensionValuescontinuityValuesおよびbiasValuesプロパティを変更することで、円弧を制御できます。より細かく制御したい場合は、values配列の代わりに独自のパスを定義できます。

pathフォローする

任意のパスを作成し、プロパティがそれに続くように指定できます。ここでは単純な円弧を使用しています

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL,
                  startPoint.x, startPoint.y);
CGPathAddCurveToPoint(path, NULL,
                      controlPoint1.x, controlPoint1.y,
                      controlPoint2.x, controlPoint2.y,
                      endPoint.x, endPoint.y);

CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:@"position"];
move.path = path;
move.duration = 2.0f;

myView.layer.position = endPoint; // instead of removeOnCompletion
[myView.layer addAnimation:move forKey:@"move the view"];
于 2013-07-11T08:36:27.350 に答える
4

これを試してみると、問題が確実に解決されます。プロジェクトでこれを使用しました。

UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10, 126, 320, 24)] autorelease];
label.text = @"Animate image into trash button";
label.textAlignment = UITextAlignmentCenter;
[label sizeToFit];
[scrollView addSubview:label];

UIImageView *icon = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"carmodel.png"]] autorelease];
icon.center = CGPointMake(290, 150);
icon.tag = ButtonActionsBehaviorTypeAnimateTrash;
[scrollView addSubview:icon];

UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.center = CGPointMake(40, 200);
button.tag = ButtonActionsBehaviorTypeAnimateTrash;
[button setTitle:@"Delete Icon" forState:UIControlStateNormal];
[button sizeToFit];
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:button];
[scrollView bringSubviewToFront:icon];

- (void)buttonClicked:(id)sender {
    UIView *senderView = (UIView*)sender;
    if (![senderView isKindOfClass:[UIView class]])
        return;

    switch (senderView.tag) {
        case ButtonActionsBehaviorTypeExpand: {
            CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
            anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
            anim.duration = 0.125;
            anim.repeatCount = 1;
            anim.autoreverses = YES;
            anim.removedOnCompletion = YES;
            anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)];
            [senderView.layer addAnimation:anim forKey:nil];

            break;
        }

        case ButtonActionsBehaviorTypeAnimateTrash: {
            UIView *icon = nil;
            for (UIView *theview in senderView.superview.subviews) {
                if (theview.tag != ButtonActionsBehaviorTypeAnimateTrash)
                    continue;
                if ([theview isKindOfClass:[UIImageView class]]) {
                    icon = theview;
                    break;
                }
            }

            if (!icon)
                return;

            UIBezierPath *movePath = [UIBezierPath bezierPath];
            [movePath moveToPoint:icon.center];
            [movePath addQuadCurveToPoint:senderView.center
                             controlPoint:CGPointMake(senderView.center.x, icon.center.y)];

            CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
            moveAnim.path = movePath.CGPath;
            moveAnim.removedOnCompletion = YES;

            CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
            scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
            scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
            scaleAnim.removedOnCompletion = YES;

            CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"alpha"];
            opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
            opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
            opacityAnim.removedOnCompletion = YES;

            CAAnimationGroup *animGroup = [CAAnimationGroup animation];
            animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
            animGroup.duration = 0.5;
            [icon.layer addAnimation:animGroup forKey:nil];

            break;
        }
    }
}
于 2013-07-11T07:57:42.133 に答える
1

やり方がわかった。X と Y を別々にアニメートすることは本当に可能です。同じ時間 (2.0 秒以下) でそれらをアニメートし、別のタイミング関数を設定すると、開始値から終了値まで直線ではなく弧を描いて移動するように見えます。アークを調整するには、別のタイミング関数を設定して遊ぶ必要があります。ただし、CAAnimation が「セクシーな」タイミング関数をサポートしているかどうかは不明です。

        const CFTimeInterval DURATION = 2.0f;
        CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
        [animation setDuration:DURATION];
        [animation setRemovedOnCompletion:NO];
        [animation setFillMode:kCAFillModeForwards];    
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
        [animation setFromValue:[NSNumber numberWithDouble:400.0]];
        [animation setToValue:[NSNumber numberWithDouble:0.0]];
        [animation setRepeatCount:1.0];
        [animation setDelegate:self];
        [myview.layer addAnimation:animation forKey:@"animatePositionY"];

        animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
        [animation setDuration:DURATION];
        [animation setRemovedOnCompletion:NO];
        [animation setFillMode:kCAFillModeForwards];    
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [animation setFromValue:[NSNumber numberWithDouble:300.0]];
        [animation setToValue:[NSNumber numberWithDouble:0.0]];
        [animation setRepeatCount:1.0];
        [animation setDelegate:self];
        [myview.layer addAnimation:animation forKey:@"animatePositionX"];

編集:

https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/CAMediaTimingFunction_class/Introduction/Introduction.html (functionWithControlPoints:::: によって開始された CAMediaTimingFunction )を使用して、タイミング関数を変更できるはずです。 3次ベジエ曲線」。私はGoogleがそこに答えを持っていると確信しています。http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves :-)

于 2012-02-13T14:57:53.230 に答える
0

数日前に少し似たような質問があり、 Bradが言うように、タイマーを使用して実装しましたが、そうではありませんNSTimerCADisplayLink-これは、アプリケーションのframeRateと同期され、よりスムーズで自然なアニメーションを提供するため、この目的で使用する必要があるタイマーです。あなたはここで私の答えでそれの私の実装を見ることができます。この手法は、実際には、よりもはるかに多くのアニメーションの制御を提供しCAAnimation、それほど複雑ではありません。 CAAnimationビューを再描画すらしないため、何も描画できません。すでに描かれているものを動かしたり、変形させたり、フェードさせたりするだけです。

于 2012-04-16T13:32:58.480 に答える