【SwiftUI 3.0】TimelineView で 「NowLoading…」画面を作成

TimelineView を使えば、任意のスケジュールで定期的に View を更新することができます

今回は試しにゲームでよくある「Now Loading…」画面を作成してみました。

動作確認環境

macOS Monterey(12.2.1)

Xcode 13.2.1

iOS 15.2(iPhone 13 mini シミュレータ)

【SwiftUI 3.0】TimelineView で 「NowLoading…」画面を作成

TimelineView の使い方

イニシャライザーは以下のように定義されています。

public init(_ schedule: Schedule, @ViewBuilder content: @escaping (TimelineView<Schedule, Content>.Context) -> Content)
  • schedule: View を更新するスケジュールを指定
  • content: 更新対象の View を指定します。
TimelineView(.periodic(from: Date(), by: 1) { context in
    Text("TimelineView")
}
periodic の引数
  • from: 開始する時間を指定
  • by: 更新する秒数を指定します。

上記の例では、1秒ごとに Text が更新されます。

schedule には periodic の他に、

毎分更新する .everyMinute

指定の時間を配列で指定する .explicit などがあります。

everyMinute で毎分固定で更新
TimelineView(.everyMinute) { context in }
explicit で 3秒後、7秒後、14秒後に更新する例
let dates = [
    Date(timeIntervalSinceNow: 3),
    Date(timeIntervalSinceNow: 7),
    Date(timeIntervalSinceNow: 14),
]
TimelineView(.explict(dates)) { context in }

「Now Loading…」画面のコード

冒頭Gif画像の全コードは以下のようになっています。

struct ContentView: View {
    
    let interval = 0.5
    let startDate = Date()
    let dots = [
        "",
        ".",
        "..",
        "...",
    ]
    
    var body: some View {
        GeometryReader { bodyView in
            VStack {
                Spacer()
                HStack {
                    Spacer().frame(width: bodyView.size.width * 0.3)
                    Text("Now Loading")
                        .font(.system(size: 24, weight: .semibold, design: .serif))
                    TimelineView(.periodic(from: startDate, by: interval)) { context in
                        let timeInterval = Date().timeIntervalSince(startDate)
                        let index = Int((timeInterval * 100) / (interval * 100)) % dots.count
                        Text(dots[index])
                            .frame(alignment: .leading)
                            .font(.system(size: 24, weight: .semibold, design: .serif))
                    }
                }
                Spacer()
            }
        }
    }
}

0.5 秒ごとに View の更新を走らせています。

timeIntervalSince で開始からの時間を取得し、インターバルの 0.5 秒で徐算し、その後配列の数で剰余を求めてインデックスを取得しています(100掛けているのは、Int にキャストしていることと小数点だと剰余計算ができないからです)

let timeInterval = Date().timeIntervalSince(startDate)
let index = Int((timeInterval * 100) / (interval * 100)) % dots.count

以上、TimelineView について紹介しました。

ちょっとしたアニメーションなど、他にも色々と用途がありそうですね。

「こんな使い方あるよ」とかコメント頂けると嬉しいです。