【Swift】ルーレットのようにぐるぐるまわす
ルーレットを作ろうということで、試行錯誤していたのですが、いくつかメモ。
【前提】スタートボタンで円状に書いた文字列と矢印をぐるぐる回し、ストップボタンを押すとゆっくり減速して止まるようにしたい。
【問題】停止する角度は後から変えられないので、ストップボタンを押した時に少しカクッとする。(矢印の位置が変わるため)
【解決法】停止前にアニメーション中の現在の角度を取得して、停止とともにその位置に移動で見栄えは大丈夫っぽい!
→厳密にはダメなんだろうが、ぐるぐる回っているので見栄えはOKっぽく見える!
以下、Stopボタン押下後の流れ
// ルーレットが回転中でなければ何もしない
guard isSpinning else { return }
// ルーレットが回転中でないことを示すフラグをリセット
isSpinning = false
//減速して止まる角度を計算
stopAngle = calculateStopAngle()
//アニメーション中の角度を取得
var stopRotationAngle2: CGFloat = 0.0
if let presentationLayer = arrowContainerView.layer.presentation() {
let transform = presentationLayer.transform
stopRotationAngle2 = atan2(transform.m12, transform.m11)
}
//アニメーション停止
arrowContainerView.layer.removeAllAnimations()
//直前の角度に回転させる
arrowContainerView.transform = CGAffineTransform(rotationAngle: stopRotationAngle2)
//減速して止まるアニメーション開始
// CABasicAnimationを作成
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.toValue = stopAngle
rotationAnimation.duration = 3.0
rotationAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
rotationAnimation.fillMode = .forwards // アニメーションが終了しても最終状態を保持
rotationAnimation.isRemovedOnCompletion = false // アニメーションが終了しても削除しない
// ルーレットとアローコンテナにアニメーションを追加
rouletteView.layer.add(rotationAnimation, forKey: "rotationAnimation")
その他、
※二つの回転させたルーレットの停止角度から停止した文字列を取得する計算で、Intに変換していたために四捨五入で実際の停止位置とずれる時がある問題があったので、Intに変換するのは全ての計算が終わってからの方がよさそう。
(この問題に気づくのに2日かかってしまった・・・)
以下、NGパターンとOKパターンのメモ。
//NGパターン:Intでくくると1個ずれる時がある(たぶん四捨五入で・・?)
prefectureIndex = Int((CGFloat.pi * 2 - adjustedStopAngle + adjustedStopAngle2) / anglePerSegment)
// インデックスが prefectures.count を超える場合の修正
if prefectureIndex >= prefectures.count {
prefectureIndex -= prefectures.count
}
//OKパターン:最後にIntにするとOK
var prefectureIndex2: Float = Float((CGFloat.pi * 2 - adjustedStopAngle + adjustedStopAngle2) / anglePerSegment)
if prefectureIndex2 >= Float(prefectures.count) {
prefectureIndex2 -= Float(prefectures.count)
}
prefectureIndex = Int(prefectureIndex2)