指の動きを追跡し、円の外側のパスに沿って UIButtons のコレクションを移動するアニメーションを理解し始めるためのちょっとしたガイダンスを探しています
私はそれがリボルバーのような感覚を持っていることを想像しています。それぞれが下部に固定されるように
またはこれらのスライドインサートの1つをスワイプするのと同じように
前もって感謝します
指の動きを追跡し、円の外側のパスに沿って UIButtons のコレクションを移動するアニメーションを理解し始めるためのちょっとしたガイダンスを探しています
私はそれがリボルバーのような感覚を持っていることを想像しています。それぞれが下部に固定されるように
またはこれらのスライドインサートの1つをスワイプするのと同じように
前もって感謝します
(サンプルコードはGitHubにあります)
それほど難しくはありません。三角法がたくさん含まれているだけです。
さて、これから説明するのはアニメーションではありません。これは、タイトルで指の位置を追跡するように要求したためです。アニメーションには独自のタイミング関数が含まれますが、タッチ ジェスチャを使用しているため、このイベントに固有のタイミングを使用して、それに応じてビューを回転させることができます。(TL;DR: ユーザーは暗黙的なタイマーではなく、移動の時間を保持します)。
まず、角度を追跡する便利なクラスを定義しましょう。これを と呼びます DialView
。これは、実際には UIView の単なるサブクラスであり、次のプロパティがあります。
@interface DialView : UIView
@property (nonatomic,assign) CGFloat angle;
@end
- (void)setAngle:(CGFloat)angle
{
_angle = angle;
self.transform = CGAffineTransformMakeRotation(angle);
}
をこのUIButton
ビューに含めることができます (ボタンを回転に使用するかどうかはわかりませんUIPanGestureRecognizer
。最も便利な方法なので、 を使用します)。
内部でパン ジェスチャを処理するビュー コントローラを作成しましょうDialView
。また、DialView への参照も保持しましょう。
@class DialView;
@interface ViewController : UIViewController
// The previously defined dial view
@property (nonatomic,weak) IBOutlet DialView *dial;
// UIPanGesture selector method
- (IBAction)didReceiveSpinPanGesture:(UIPanGestureRecognizer*)gesture;
@end
パンジェスチャをどのように接続するかはあなた次第です。個人的には、ペン先ファイルで作成しました。さて、この関数の本体:
- (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 ...)
}
}
CGPointAngle
atan2
は私が発明したメソッドであり、 (今すぐ呼び出すと、私はスロー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 つの角度があることです。
この場合、ビューの角度を にしたいと考えていますoriginalAngle + deltaFinger
。それが上記のコードで行われていることです。すべての状態を構造体にカプセル化するだけです。
「ビューの境界」を追跡したい場合は、メソッドを使用して、と のCGPointDistance
間の距離が特定の値であるかどうかを確認する必要があります。begin.center
now.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
単なる代用です。方程式は実際には非常に単純で、丸め関数を悪用して閉角を近似するだけです。