はじめに
業務では受託案件を担当しているということもあり,
アニメーションを使うことはほぼないです。(提案すればあるいは)
個人アプリでもあまり構想に入っていなくて使ってないです。
妄想や気になるテーマを実際に実装してみる個人アプリがあるのですが,
あるテーマの実装中にふと使いたくなって確認してみたら少し困ったので備忘録です。
追加しようと思ったのは UIImageView
をアニメーションで 1 回転させる
というただそれだけの実装で,回転の方はアフィン変換の
CGAffineTransform
を使い角度は 360° を指定すればいいかな。
アニメーションの方は UIView
のアニメーションのメソッド
(animateWithDuration:animations:completion:
)を使えば
すぐ実現できるだろうと思ったけどうまくいかなかった。
要件
あるアイテムのセルをタップしたらお気に入り状態になり,
星の画像がアニメーションで 1 回転しながら黄色くなってほしい。
※妄想なのでお気に入り登録しても各路線のニュースは届きません
CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
で
360° を指定してもアニメーションしているように見えない。
いきなり 360° ではなく複数回,角度に分けて実装したら
アニメーションありで回転してくれました。
実装
開発環境
- Xcode 9.2
- Swift 4
360° 回転するだろうと思った実装
下記のようなコードで最初は大丈夫だろうと思っていた。
let isFavorite: Bool = /* お気に入り状態の値取得 */
if (isFavorite) {
// お気に入りだった場合,枠のみの星アイコンを指定
cell.favoImageView.image = UIImage(named: "ic_vacant_star")
} else {
// アニメーションしながら 1 回転->360°を指定
UIView.animate(withDuration: 0.5, animations: {
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
// アニメーション終了後に満たされた星アイコンを表示
cell.favoImageView.image = UIImage(named: "ic_filled_star")
})
}
/* お気に入り状態の更新処理 */
結果
星に色はつくけれどもアニメーションしているように見えない。
ちなみに 180° を指定すると・・・
ちゃんとアニメーションはしている。
360度回転させる実装
180° のアニメーションはできているから,
180° 回転させた後 360° を指定して 2 回に分けてみたらどうか。
let isFavorite: Bool = /* お気に入り状態の値取得 */
if (isFavorite) {
cell.favoImageView.image = UIImage(named: "ic_vacant_star")
} else {
UIView.animate(withDuration: 0.5, animations: {
// 一旦 180° 回転させる
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
// 後は同じで 360° を指定する
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
cell.favoImageView.image = UIImage(named: "ic_filled_star")
})
}
/* お気に入り状態の更新処理 */
結果
回転しているように見える。求めていた挙動が得られた。
[+α]360°時計回りに回して逆時計回りに 360° 回す
アニメーションの逆再生のオプションである,
UIViewAnimationOptionAutoreverse
を使う。
let isFavorite: Bool = /* お気に入り状態の値取得 */
if (isFavorite) {
cell.favoImageView.image = UIImage(named: "ic_vacant_star")
} else {
UIView.animateKeyframes(withDuration: 0.5, delay: 0.0, options: [.autoreverse], animations: {
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
cell.favoImageView.image = UIImage(named: "ic_filled_star")
})
}
/* お気に入り状態の更新処理 */
結果
どうやるんだろうと思っただけでさすがに UX 悪そうなので見送り・・・
[+α]複数回回転させる ~単純に~
ぐるんぐるん回転させてみたい。
例えば 3 回転させる場合は,単純に考えると
アニメーションが終わったらアニメーションさせてを繰り返す。
でもさすがにこれだと・・・
let isFavorite: Bool = /* お気に入り状態の値取得 */
if (isFavorite) {
cell.favoImageView.image = UIImage(named: "ic_vacant_star")
} else {
UIView.animate(withDuration: 0.5, animations: {
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
UIView.animate(withDuration: 0.5, animations: {
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
UIView.animate(withDuration: 0.5, animations: {
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
cell.favoImageView.image = UIImage(named: "ic_filled_star")
})
})
})
}
/* お気に入り状態の更新処理 */
結果
こうなるだろうなという結果で回転後の次の回転まで若干ラグい。
[+α]複数回回転させる ~Repeatオプション~
アニメーションのオプションで UIViewAnimationOptionRepeat
を使ってみる。
let isFavorite: Bool = /* お気に入り状態の値取得 */
if (isFavorite) {
cell.favoImageView.image = UIImage(named: "ic_vacant_star")
} else {
UIView.animate(withDuration: 0.5, delay: 0.0, options: [.repeat], animations: {
// リピート回数は 3 回にする
UIView.setAnimationRepeatCount(3)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
}, completion: { (_) in
cell.favoImageView.image = UIImage(named: "ic_filled_star")
})
}
/* お気に入り状態の更新処理 */
結果
3 回転というか 180° 回転を 3 回しているように見える。
でも最後の位置は正しい・・・
それぞれの CGAffineTransform
を作って,
concatenating
でまとめてもなんかおかしい。
もうちょっと調べてみる。。。
他と組み合わせた最終的な実装
試行錯誤して最終的に少しインパクト与えるために途中で大きくして
アニメーション終わったら元のサイズに戻す実装も追加してみて
下記のような感じにしました。
let isFavorite: Bool = /* お気に入り状態の値取得 */
if (isFavorite) {
cell.favoImageView.image = UIImage(named: "ic_vacant_star")
} else {
UIView.animate(withDuration: 0.5, animations: {
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*180)
cell.favoImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*360)
cell.favoImageView.image = UIImage(named: "ic_filled_star")
cell.favoImageView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}) { (_) in
cell.favoImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
}
}
/* お気に入り状態の更新処理 */
結果
なんか最低限のインパクトはあるような気がする。
もっと複雑なものも作ってみたいけど今回はこんなところでいいかな。
おわりに
今回は UIImageView をアニメーションで
360° 回転させてみる実装について書きました。
こうすればもっと良くなるよ,こう実装すべきなど
ありましたらアドバイスお願いします!
アニメーションってすごく奥深いなぁと思うと同時に
多用は UX 的に良くないなーと思いました。
ご覧いただきありがとうございます。