ボリュームのつまみを模倣した UI の作り方。UIGestureRecognizer のサブクラスを作って利用する。
ポイントは2つあって、1つめはジェスチャーを受け付ける範囲の限定。
緑帯の範囲だけジェスチャーを受け付けたい。その為には中心からタッチした点までの距離がわかればいい。
2つめは角度の計算。ドラッグしている最中に始点と終点からその間の角度を計算する。これは中心から2点への線からアークタンジェントでそれぞれの角度を求め、その差をとればいい。
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA, CGPoint endLineA, CGPoint beginLineB, CGPoint endLineB) { CGFloat a = endLineA.x - beginLineA.x; CGFloat b = endLineA.y - beginLineA.y; CGFloat c = endLineB.x - beginLineB.x; CGFloat d = endLineB.y - beginLineB.y; CGFloat atanA = atan2(a, b); CGFloat atanB = atan2(c, d); // convert radiants to degrees return (atanA - atanB) * 180 / M_PI; }
Github からデモコードが入手できる。
melle/OneFingerRotationGestureDemo - GitHub
実行するとこんな感じ。
実機で確認したが悪くない。気になる点としては限定された円の帯から指が外れると回転できないこと(上図、緑色の帯の範囲)。この判定は最初のタッチの時には有効としてもいいが、回転中は多少外れても有効にした方が使い勝手はいいと思う。後は円内の丸点以外でも回転を受け付けるところか。これはアプリデザインにもよる。それにしても UIGestureRecognizer をビューへ登録する仕組みは便利。今回のようにカスタムジェスチャーを作れば、容易にビューへ組み込める。
なんでangleBetweenLinesInDegreesの引数が4つなんだろう?と思ってリンク元を見てみました。おそらくこの関数は元々はくっついてない2線の角度を求めるものなんでしょうね。今回だとbeginLineAとbeginLineBは同一でしょうから今回用に作ると引数は3つでしょうね。
touchMovedで帯の外側も受け付けるのは同意です。内側をどうするかは作りながら丁度よいところを探っていくやり方になるんでしょうね。
ありがとうございました。
こんにちは。
引数が4つの件、指摘の通りですね。
今回の場合は始点が同じなので確かに引数は3つあれば
いいのですが汎用的に作ってあるのだと思います。
では。