この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
無性にTimer
を使ってコマ送りアニメを作りたかったので作ってみました。
環境
- Xcode 13.2.1
デモ
作ったものはこんな感じのコマ送りです。
アセット
proposal0
~ proposal8
までの計9の画像を用意しました。
コード
ContentView
のコードはこのようになっています。
import SwiftUI
struct ContentView: View {
@State var index: Int = 0
private let timer = Timer.publish(every: 0.3, on: .main, in: .common).autoconnect()
var body: some View {
Image("proposal\(index)")
.resizable()
.frame(width: 300, height: 300)
.padding()
.onReceive(timer) { _ in
index += 1
if index >= 8 {
timer.upstream.connect().cancel()
}
}
}
}
index
今回コマ送りに使用するImage
のインデックスとして使用します。
@State var index: Int = 0
@State
をプロパティに付けることで、そのプロパティの変更がSwiftUIによってモニタリングされます。
モニタリングしたプロパティに変更があった場合、対象プロパティを参照しているViewは自動的に再描画されるので、index
の値が増える度に画像が切り替わっていくような動きを実現できます。
Timer
カウントダウンアプリなどで使われるTimer
です。scheduledTimer
がお馴染みですが、今回はTimerPublisher
を使ってみました。
private let timer = Timer.publish(every: 0.3, on: .main, in: .default).autoconnect()
このTimerPublisher
は現在の時刻を与えられた間隔で繰り返しpublishしてくれるPublisher
になります。
ちなみにpublishとは、発行する、発表するという意味になります。
このTimerPublisher
がイベントの発行を開始するには、autoconnect()
またはconnect()
を呼ぶ必要があり、autoconnect()
を呼ぶことで接続処理を自動的に行なってくれます。
publish
Timer.publish
の引数についてですが、下記にようになっています。
static func publish(every interval: TimeInterval, tolerance: TimeInterval? = nil, on runLoop: RunLoop, in mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil) -> Timer.TimerPublisher
- every : 何秒毎にイベントを発行するか
- tolerance : この値を設定するとTimerは発火のタイミングを遅らせることが出来ます。デフォルト値は
nil
- on :
RunLoop
を実行するスレッド - mode :
RunLoop.Mode
の設定 - options : 実行するループスケジューラーの任意オプションになります。
今回は、Timer.publish(every: 0.3, on: .main, in: .common)
と使用していますが、0.3
秒毎にmain
スレッドでcommon
のモードでイベントを発行するという意味になります。
Image
コマ送りを表現するためのImage
です。index
の値が変更されるとImage
も変更されます。
Image("proposal\(index)")
.resizable()
.frame(width: 300, height: 300)
.padding()
onReceive
onRecieve
は第一引数にPublisher
を渡すことで、クロージャーの中ではそのPublisher
がイベントを発行する度に実行したい処理を追加します。
今回は、純粋にindex
を増やす処理を記述しています。また、画像はproposal8
までしかないのでindex
が8
以上になるとtimer.upstream.connect().cancel()
でTimerPublisher
のイベント発行をキャンセルしています。
.onReceive(timer) { _ in
index += 1
if index >= 8 {
timer.upstream.connect().cancel()
}
}
おわりに
Timer
を使ってコマ送りアニメを作ることができました!
さて、プロポーズの結果はいかに?