STARTボタンを押すとタイマーを再セットする
STARTボタンを押すとタイマーが開始するところまで実装ができましたが、実はSTARTボタンを押した後に再度STARTボタンを押すと残り時間のカウントダウンの数字が速いペースで減っていくようになってしまいます。
これは二重にタイマーが実行されていることが原因で発生しています。なので、タイマーが開始した後にSTARTボタンを押した場合に再度タイマーが180から再セットされるように関数startTimerのコードを変更します。
func startTimer() {
// ここを追加↓
timer?.invalidate()
remainingTime = 180
//
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
remainingTime -= 1
if remainingTime == 0 {
timer?.invalidate()
remainingTime = 180
}
})
}
タイマーが0になった時の処理と同様に、タイマーを無効化して180の値を再セットしています。これでタイマーが開始した後に、STARTボタンを押しても再度180からカウントダウンできるようになりました。
繰り返し使用している処理を関数にする
関数の説明でプログラム内で繰り返し使える処理をひとまとまりにできるとお伝えしました。この関数startTimerの中を見てみると重複したコードが確認できます。
func startTimer() {
// 同じ処理
timer?.invalidate()
remainingTime = 180
//
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
remainingTime -= 1
if remainingTime == 0 {
// 同じ処理
timer?.invalidate()
remainingTime = 180
//
}
})
}
この繰り返し使用しているコードを切り出した関数resetTimerを作成します。また、繰り返し同じコードを書いていた箇所をこの関数に置き換えます。
func startTimer() {
// ここを追加↓
resetTimer()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
remainingTime -= 1
if remainingTime == 0 {
// ここを追加↓
resetTimer()
}
})
}
// ここを追加↓
func resetTimer() {
timer?.invalidate()
remainingTime = 180
}
コードは変わりましたが、アプリの動きに変化はないため、STARTボタンを何度押しても残り時間が再セットされることを確認できます。
残り時間を分かりやすい形式に変換する
タイマーの機能はできましたが、アプリ上に表示されている180.000000という残り時間をもう少し分かりやすい形式に変換していきます。
Swiftには分かりやすい形式に変換してくれるフォーマッターが用意されています。今回はその中から時間量を変換してくれるDateComponentsFormatterを使用します。まずは次のようにDateComponentsFormatterを追加してください。
struct CupRamenTimerView: View {
@State private var timer: Timer?
@State private var remainingTime: Double = 180
// ここを追加↓
let dateFormatter: DateComponentsFormatter = {
let dateFormatter = DateComponentsFormatter()
dateFormatter.unitsStyle = .short
dateFormatter.allowedUnits = [.minute, .second]
return dateFormatter
}()
まずは今回使用した定数の定義方法を説明します。
let 定数名: 型 = {
//生成時に実行する処理
}()
これは、定数を初期化する際に特定の処理を実行するための方法です。初期化時に一度だけ{}内の処理を実行し、その結果を定数として保持することができます。
定数dateFormatterの初期化時に実行している処理は次の通りです。
-
DateComponentsFormatterを生成する -
unitsStyleで変換する時間量の表示形式を短い形式になるように設定する -
allowedUnitsで表示する単位を分と秒のみが表示されるように設定する
これでフォーマッターの用意はできました。次にアプリ上に表示する文字をこのdataFormatterを使用して次のように変換します。
var body: some View {
ZStack {
Color.black
.ignoresSafeArea()
VStack(spacing: 24) {
Text("3分間カップラーメンタイマー")
.font(.title3)
.bold()
.foregroundStyle(.white)
// ここを変更↓
Text("\(dateFormatter.string(from: remainingTime) ?? "180")")
.font(.system(size: 60))
.fontWeight(.black)
.foregroundStyle(.yellow)
Button("START") {
startTimer()
}
.font(.largeTitle)
}
}
}
これでプレビューには3minと残り時間が表示されるようになりました。

変更したコードの詳細を説明すると、Textの値として、dataFormatterに残り時間の値を渡して、フォーマットされた文字列を取得しています。ただ、関数dateFormatter.string(from:)の戻り値がString?でOptionalになっているため、そのままではTextに反映することができません。??の記号を用いることで、値が空の場合に任意の値を設定することができます。
以上でカップラーメンタイマーの完成です。
まとめと次回
今回はアプリを作る過程で、ZStackを使って背景色をつける方法やif文や関数、Timerを使用する方法を学びました。
今回は、3分間のみしか計測できないため、5分間を必要とするカップラーメンには対応していません。ぜひ、5分間のカップラーメンタイマー作成にもチャレンジしてみてください!
もし、アプリのデザインについてもっと学びたいと感じはじめた方がいましたら、Apple Human Interface GuidelineでAppleのガイドラインを確認することをお勧めします。
次回はアプリ「福笑いゲーム」を作ります。お楽しみに!

