watchOSアプリを開発した際に、いくつかアニメーションをつくりました
その中でうまくいったこと😹いかなかったこと😹があるので、そのまとめになります
基本のローディングアニメーション
実装方法は以下の2つ
- パラパラ画像をつかう
- APNGをつかう
パラパラ画像をつかう
Appleのサンプルコード的にも正攻法といって良いでしょう
特徴としては、
- Asset Catalogを利用できるため、解像度の対応がしやすい
- 画像枚数が増えると管理が手間(あくまでAPNGにくらべて)
です
シンプルな Duration 指定の場合
// animatedImage: WKInterfaceImage
animatedImage.setImage(UIImage(named: "Bus"))
animatedImage.startAnimating()
startAnimating
はStoryboard上で設定されたDurationでアニメーションを行います
Duration + Repeat Count 指定の場合
animatedImage.setImage(UIImage(named: "Bus"))
animatedImage.startAnimatingWithImages(in: NSMakeRange(0, 4), duration: 2.0, repeatCount: 3)
APNGをつかう
APNGはアニメーションするPNG画像のことです。
特徴としては、
- リソースが1つになるので、画像パラパラよりも管理が楽
- 画像は、Extension ターゲットのリソースに入れる
- APNGの尺に合わせたDurationにすれば良い
- watchOSはAPNG対応のライブラリがない(*注)ので自前でソースコードを書く必要がある
*注:下書き時点ではなかったのですが、今ならSDWebImage/SDWebImageSwiftUIが使えるかもしれません
実装例については、Apple Watch で GIF/APNG を使う (ライブラリ不使用) が参考になりました(ありがとうございました)
こちらでスムーズにアニメーションができました
カウントダウンで遷移アニメーション😹
やりたかったのは「3 -> 2 -> 1 -> 画面遷移」でした
前述の「パラパラ画像もしくはAPNGをつかい、画面側でタイマーセットをし一定時間後に画面遷移する」という実装を行いましたが、カウントダウンし切る前に遷移してしまったり・・・とうまくいきませんでした
(こちらについて、シンプルで良い方法があればご教授いただけるとうれしいです😹)
より動的なアニメーション
たとえば位置情報に基づいてアニメーションを移動・変更したい等のケースでは、Sprite Kit を使うことになると思います
ただし、watchOSで複雑なアニメーションをする際には、パフォーマンスに気を配るとよさそうです
(特にApple Watch Series 1などでは、最悪の場合、いつの間にかアプリがパージされます😹)
やるといいこと
スレッド管理
断然 ReactiveX/RxSwift の利用が楽です
- アニメーションの移動についての計算はバックグラウンド処理
- 描画処理はメインスレッドで実行
という実装にしましょう
SKTextureAtlas の利用
公式ドキュメントは About Texture Atlases
SKTextureAtlasの利用により、メモリ使用量、描画パフォーマンスが向上するようです
まず、使用するテクスチャについては、Asset Catalog > + > New Sprite Atlas で専用フォルダをつくり、格納しましょう
テクスチャアトラスからテクスチャを作成
static let TextureAtlasName = "Sprite Atlasのフォルダ名"
static let TextureAtlas = SKTextureAtlas(named: TextureAtlasName)
lazy var textures: [SKTexture] = {
var textures = [SKTexture]()
let numberOfTextures = TextureAtlas.textureNames.count - 1
for index in 0...numberOfTextures {
let name = String(format: "%@%d", TextureAtlasName, index)
textures.append(TextureAtlas.textureNamed(name))
}
return textures
}()
テクスチャの切替のみ(要はパラパラ)でアニメーションする場合は以下のような実装
func animate() {
let node = SKSpriteNode(..省略..) // アニメーション大賞のSKSpriteNodeオブジェクト生成
let action = SKAction.animate(with: textures, timePerFrame: 0.03)
let repeatAction = SKAction.repeatForever(action)
node.run(repeatAction, withKey: key) // node: SKSpriteNode
}
画像比率の適用
Sprite Kitを使う際にはAutoLayoutやCore Graphicsのような簡単な配置ができません
そのため、全端末でうつくしく表示するためには、画像比率に対してテクスチャサイズの変更等が必要かもしれません
たとえば、背景画像を画面いっぱいに表示したい場合は以下のように SKScene のサイズを指定します
func setupScene() {
let image: UIImage = BackgroundImage
let size = contentFrame.size
let portraitRatio = size.height / image.size.width
let landscapeRatio = size.width / image.size.height
let multiplier = min(portraitRatio, landscapeRatio)
let sceneSize = CGSize(
width: image.size.width * multiplier,
height: image.size.height * multiplier)
let scene = SKScene(size: sceneSize)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.scaleMode = .aspectFit
sceneInterface.presentScene(scene)
}
以降、このScene上で配置するNodeのサイズは、multiplier
を使った計算が必要になるでしょう
まとめ
watchOS6よりWatch-only appの開発が可能になりました。
まだアプリ数は少ないようですが、AppleWatch用のApp Storeもリリースされました。
これからwatchOSアプリ市場が盛り上がることを期待!
以上です🎉🎉🎉
ありがとうございました😽
Thanks to
SpriteKit Animations and Texture Atlases in Swift
Apple Watch で GIF/APNG を使う (ライブラリ不使用)
WatchKit のカスタムUI実現方法のまとめ