[SwiftUI] Slider のつまみの位置を Timer の進捗状況に合わせて動かす

SwiftUI SwiftUI
スポンサーリンク

概要

本記事では、Slider のつまみの位置を Timer の進捗状況に合わせて動かす方法について説明します。

スポンサーリンク

環境

OS: macOS Monterey 12.3.1
Xcode: 13.3.1
Swift: 5.6.0.323.62

スポンサーリンク

Slider の基本

Slider は、過去記事「Slider の使用方法」で既に説明していますが、簡単に説明すると「つまみをスライドさせて値を変化させる」UI部品です。

通常、つまみはユーザーが手動で動かします。

Slider の基本的な構文は以下の通りです。
Slider の value には、つまみの現在値、in にはつまみの最小値から最大値までの範囲を指定します。

Slider(value: 値, in: 範囲)

Slider の基本使用例を以下に示します。

この例では、Slider の範囲を 0 〜 100 までにして、つまみを動かすと、現在の値が Text に表示されます。

struct ContentView: View {
    @State private var sliderVal : Double = 0
    
    var body: some View {
        VStack() {
            Text(String(format: "%.1f", sliderVal))
            Slider(value: $sliderVal, in: 0...100)
            .padding(.horizontal)
            Spacer()
        }
    }
}
Slider の基本使用例

Slider の基本使用例

スポンサーリンク

つまみの位置をタイマーと連動する

Slider の基本を理解したところで、つまみの位置とタイマーを連動する方法について見ていきます。

Timer.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

パラメータは以下の通りです。

パラメータ 説明
interval イベントを発行する時間間隔。たとえば、0.5 を指定すると0.5秒ごとにイベントを発行します。
tolerance イベントを発行するときに許可されるタイミングの差異。デフォルトはnil です。これにより任意の分散が可能になります。
runLoop タイマーが実行される実行ループ。
mode タイマーを実行する実行ループモード。
options タイマーに渡されるスケジューラオプション。デフォルトはnil。

以下にコード例を示します。

この例では、アプリが起動するととともに、タイマーがスタートします(3行目)。

タイマーの進捗状況は .onReceive で受け取ることができ、Slider の値を更新しています(10行目)。

struct ContentView: View {
    @State private var sliderVal = 0.0
    let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()

    var body: some View {

        VStack {
            Text(String(format: "%.1f", sliderVal))
            Slider(value: $sliderVal, in: 0...100)      // 0から100の範囲を指定
                .onReceive(timer) { _ in
                    if sliderVal < 100 {
                        sliderVal += 1
                    }
            }
            Spacer()
        }

    }
}
タイマーとつまみを連動する例

タイマーとつまみを連動する例

スポンサーリンク

[Start][Pause][Stop]ボタンを配置し、タイマーに合わせてつまみを動かす

先ほどの例では、アプリの起動と同時に、タイマーがスタートしていました。

今度は、任意のタイミングでタイマーの開始ができるようにしていきます。

ストップウォッチクラスを作成する

せっかくですので、Timer を利用して、開始、一時停止、停止ができるストップウオッチクラスを作成します。

コードは以下の通りです。

//
//  StopWatch.swift
//  SliderAndTimer
//
//  Created by 高橋広樹 on 2022/05/14.
//

import Foundation

class StopWatch:ObservableObject{
    
    // ストップウォッチの状態
    enum status {
        case start
        case stop
        case pause
    }
    
    @Published var currentStatus:status = .stop
    @Published var elapsedTime = 0.0
    
    var timer = Timer()
    
    // ストップウォッチの開始
    func start(maxTime : Double = 1.0){
        currentStatus = .start
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true){ timer in
            self.elapsedTime += 0.1
            
            if maxTime > 0.0 && self.elapsedTime > maxTime {
                self.stop()
            }           }
        }
    }
    
    // ストップウォッチの停止
    func stop(){
        timer.invalidate()
        elapsedTime = 0
        currentStatus = .stop
    }
    
    // ストップウォッチの一時停止
    func pause(){
        timer.invalidate()
        currentStatus = .pause
     }
}

13〜17行目は、ストップウオッチの状態を表す enum です。ストップウオッチが開始しているのか、一時停止中なのか、停止中なのかを表します。

19行目は、現在の状態を管理する変数で、データ型は先ほどの enum の status 型です。

20行目は、現在のストップウオッチの経過時間を表します。

この StopWatch クラスには、start(), stop(), pause() のメソッドがあり、それぞれのメソッドが実行されると、タイマーの開始、停止、一時停止が行われます。

タイマーを開始する start() メソッドは 25〜34行目です。

メソッドの引数 maxTime はタイマーの長さ(時間)を秒で指定することができます。引数で指定した時間に到達すると、タイマーは自動で停止します。引数に時間指定がない場合は、stop() メソッドが実行されるまでストップウオッチは動き続けます。

タイマーを停止する stop() メソッドは37〜41行目です。

タイマーを停止するために timer.invalidate() メソッドを実行した後、経過時間 elapsedTime を 0 にしています。

タイマーを一時停止する pause() メソッドは44〜47行目です。

タイマーを一時停止するために timer.invalidate() メソッドを実行します。 経過時間 elapsedTime の値は保持したままにしたいので、ここでは何の操作もしていません。

タイマーに合わせてつまみを動かす

作成した stopWatch クラスを利用して、タイマーに合わせてつまみを動かすアプリを作成します。

このアプリには、[Start][Pause][Stop]ボタンを配置し、[Start]が押されるとタイマーがスタートして、タイマー値に合わせてつまみが移動します。

また、[Pause]ボタンでタイマーが一時停止、[Stop]ボタンでタイマーが停止します。

コード例を以下に示します。

struct ContentView: View {
    @State private var sliderVal = 0.0
    @ObservedObject var stopWatch = StopWatch()

    var body: some View {

        VStack {
            
            HStack {
                Button("Start") {
                    stopWatch.start()
                }
                
                Button("Pause") {
                    stopWatch.pause()
                }
                
                Button("Stop") {
                    stopWatch.stop()
                }

            }
            
            Text(String(format: "%.1f", stopWatch.elapsedTime))
            Slider(value: $stopWatch.elapsedTime, in: 0...10)      // 0から100の範囲を指定
                .padding()
            
            Spacer()
        }
    }
}

 

SwiftUITips
スポンサーリンク
スポンサーリンク
Swift Life

コメント

タイトルとURLをコピーしました