5

指の動きを追跡し、円の外側のパスに沿って UIButtons のコレクションを移動するアニメーションを理解し始めるためのちょっとしたガイダンスを探しています

ここに画像の説明を入力

私はそれがリボルバーのような感覚を持っていることを想像しています。それぞれが下部に固定されるように

またはこれらのスライドインサートの1つをスワイプするのと同じように

ここに画像の説明を入力

前もって感謝します

4

1 に答える 1

6

(サンプルコードはGitHubにあります)

それほど難しくはありません。三角法がたくさん含まれているだけです。

さて、これから説明するのはアニメーションではありません。これは、タイトルで指の位置を追跡するように要求したためです。アニメーションには独自のタイミング関数が含まれますが、タッチ ジェスチャを使用しているため、このイベントに固有のタイミングを使用して、それに応じてビューを回転させることができます。(TL;DR: ユーザーは暗黙的なタイマーではなく、移動の時間を保持します)。

指の追跡

まず、角度を追跡する便利なクラスを定義しましょう。これを と呼びます DialView。これは、実際には UIView の単なるサブクラスであり、次のプロパティがあります。

DialView.h

@interface DialView : UIView

@property (nonatomic,assign) CGFloat angle;

@end

DialView.m

- (void)setAngle:(CGFloat)angle
{
    _angle = angle;
    self.transform = CGAffineTransformMakeRotation(angle);
}

をこのUIButtonビューに含めることができます (ボタンを回転に使用するかどうかはわかりませんUIPanGestureRecognizer。最も便利な方法なので、 を使用します)。

内部でパン ジェスチャを処理するビュー コントローラを作成しましょうDialView。また、DialView への参照も保持しましょう。

MyViewController.h

@class DialView;

@interface ViewController : UIViewController

// The previously defined dial view
@property (nonatomic,weak) IBOutlet DialView *dial;

// UIPanGesture selector method    
- (IBAction)didReceiveSpinPanGesture:(UIPanGestureRecognizer*)gesture;

@end

パンジェスチャをどのように接続するかはあなた次第です。個人的には、ペン先ファイルで作成しました。さて、この関数の本体:

MyViewController.m

- (IBAction)didReceiveSpinPanGesture:(UIPanGestureRecognizer*)gesture
{
    // This struct encapsulates the state of the gesture
    struct state
    {
        CGPoint touch;          // Current touch position
        CGFloat angle;          // Angle of the view
        CGFloat touchAngle;     // Angle between the finger and the view
        CGPoint center;         // Center of the view
    };
    
    // Static variable to record the beginning state
    // (alternatively, use a @property or an _ivar)
    static struct state begin;
    
    CGPoint touch = [gesture locationInView:nil];
    
    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        begin.touch  = touch;
        begin.angle  = self.dial.angle;
        begin.center = self.dial.center;
        
        begin.touchAngle = CGPointAngle(begin.touch, begin.center);
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        struct state now;
        now.touch   = touch;
        now.center  = begin.center;
        
        // Get the current angle between the finger and the center
        now.touchAngle = CGPointAngle(now.touch, now.center);
        
        // The angle of the view shall be the original angle of the view
        // plus or minus the difference between the two touch angles
        now.angle = begin.angle - (begin.touchAngle - now.touchAngle);
        
        self.dial.angle = now.angle;
    }
    else if (gesture.state == UIGestureRecognizerStateEnded)
    {
        // (To be Continued ...)
    }
}

CGPointAngleatan2は私が発明したメソッドであり、 (今すぐ呼び出すと、私はスローCGPointDistanceします!)の素敵なラッパーです:

CGFloat CGPointAngle(CGPoint a, CGPoint b)
{
    return atan2(a.y - b.y, a.x - b.x);
}

CGFloat CGPointDistance(CGPoint a, CGPoint b)
{
    return sqrt(pow((a.x - b.x), 2) + pow((a.y - b.y), 2));
}

ここで重要なのは、追跡する 2 つの角度があることです。

  1. ビュー自体の角度
  2. 指とビューの中心との間で形成される角度。

この場合、ビューの角度を にしたいと考えていますoriginalAngle + deltaFinger。それが上記のコードで行われていることです。すべての状態を構造体にカプセル化するだけです。

半径の確認

「ビューの境界」を追跡したい場合は、メソッドを使用して、と のCGPointDistance間の距離が特定の値であるかどうかを確認する必要があります。begin.centernow.finger

それはあなたのための宿題です !

スナップバック

さて、この部分はユーザーが制御できなくなったので、実際のアニメーションです。

スナップは、一連の角度を定義することで実現できます。指が指を離すと (ジェスチャーが終了すると)、次のように元の角度に戻ります。

else if (gesture.state == UIGestureRecognizerStateEnded)
{
    // Number of "buttons"
    NSInteger buttons = 8;
    
    // Angle between buttons
    CGFloat angleDistance = M_PI*2 / buttons;
    
    // Get the closest angle
    CGFloat closest = round(self.dial.angle / angleDistance) * angleDistance;
    
    [UIView animateWithDuration:0.15 animations:^{
        self.dial.angle = closest;
    }];
}

変数は、ビューにあるボタンの数のbuttons単なる代用です。方程式は実際には非常に単純で、丸め関数を悪用して閉角を近似するだけです。

于 2013-07-30T05:46:29.087 に答える