##内容
タイマーアプリを作成するためのポイントを複数記事に分けて掲載しています。
この記事では、タイマーのカウントダウンが 0 に到達したときに発動するアラームやバイブレーションの実装について掲載します。
##環境
- OS: macOS 10.15.7 (Catalina)
- エディタ: Xcode 12.1
- 言語: Swift
- 主な使用ライブラリ: SwiftUI
##Gitリポジトリ
以下のGitリポジトリのURLからサンプルコードをご覧いただけます。
https://github.com/msnsk/Qiita_Timer.git
##手順
- TimeManager で AudioToolbox ライブラリをインポートする
- TimeManager にデフォルトのアラーム音名とアラーム ID のプロパティを追加する
- MainView で AudioToolbox ライブラリをインポートする
- MainView の ZStack の.onReceive モディファイアにアラームの発動を実装する
- MainView の ZStack の.onReceive モディファイアにバイブレーションの発動を実装する
###1. TimeManagerでAudioToolboxライブラリをインポートする
TimeManagerに新たにAudioToolboxというライブラリをインポートします。
AudioToolboxライブラリには、主に効果音的サウンドに関するクラス、プロパティ、メソッドが含まれますので、タイマーのカウントダウンが0に達したときにアラーム音を慣らすためにこれを利用します。
import SwiftUI
import AudioToolbox //追加でインポート
class TimeManager: ObservableObject {
//(プロパティ、メソッド省略)
}
###2. TimeManager にデフォルトのアラーム音名とアラーム ID のプロパティを追加する
AudioToolbox に含まれる音源を利用するには、その音源の SystemSoundID を指定する必要があります。SystemSoundID はひとつのデータ型になっており、UInt32 の typealias です。
ですので、TimeManager クラスにこのデータ型のプロパティを作成し、@Published のプロパティラッパーをつけて、デフォルトで指定したい音源の ID を値として代入します。
どの ID がどんな音なのかは、以下のリソースが参考になります。
https://github.com/TUNER88/iOSSystemSoundsLibrary
このリソースの項目をひとつずつ確認してみましたが、iOS デバイスのかなり古い機種で利用されていた通知音のように思います。最近の機種でデフォルトで採用されている通知音の情報は見つかりませんでした。ご了承ください。(ご存知の方がいればコメントいただけると幸いです)
プロパティ名は soundID とし、ここでは、個人的に心地よい音源だと感じたので、1151 を ID として代入しました。
import SwiftUI
import AudioToolbox //追加でインポート
class TimeManager: ObservableObject {
//(他のプロパティ省略)
//AudioToolboxに格納された音源を利用するためのデータ型でデフォルトのサウンドIDを格納
@Published var soundID: SystemSoundID = 1151
//(メソッド省略)
}
###3. MainView で AudioToolbox ライブラリをインポートする
手順 1 のTimeManager と同様に、MainView のほうにも AudioToolbox ライブラリをインポートします。
こちらで AudioToolbox をインポートする理由は、実際にカウントダウンタイマーが0に達したタイミングでアラート音を鳴らすメソッドを MainView に記述する必要があり、そのメソッドが AudioToolbox に含まれるからです。
import SwiftUI
import AudioToolbox //追加インポート
struct MainView: View {
//(プロパティ、メソッド省略)
}
###4. MainView の ZStack の .onReceive モディファイアにアラームの発動を実装する
MainView の ZStack の .onReceive モディファイア内で、すでに残り時間が 0 より大きいか、0 以下の場合で if-else 文による条件分岐をさせていました。その else 文(残り時間が0以下の場合)の中にアラーム音発動用のメソッドを追加します。
メソッドは以下のように記述します。第一引数に、手順2で先に用意した TimeManager クラスの soundID プロパティを指定します。第二引数は nil で大丈夫です。
AudioServicesPlayAlertSoundWithCompletion(self.timeManager.soundID, nil)
struct MainView: View {
//(プロパティ省略)
var body: some View {
ZStack {
//(省略)
}
//指定した時間(1秒)ごとに発動するtimerをトリガーにしてクロージャ内のコードを実行
.onReceive(timeManager.timer) { _ in
//タイマーステータスが.running以外の場合何も実行しない
guard self.timeManager.timerStatus == .running else { return }
//残り時間が0より大きい場合
if self.timeManager.duration > 0 {
//残り時間から -1 する
self.timeManager.duration -= 1
//残り時間が0以下の場合
} else {
//タイマーステータスを.stoppedに変更する
self.timeManager.timerStatus = .stopped
//アラーム音を鳴らす
AudioServicesPlayAlertSoundWithCompletion(self.timeManager.soundID, nil)
}
}
}
}
###5. MainView の ZStack の .onReceive モディファイアにバイブレーションの発動を実装する
アラーム音の次はバイブレーションの実装です。これもメソッドはアラーム音発動と同じものを使います。引数が変わります。
引数には kSystemSoundID_Vibrate プロパティによって得られたバイブレーション用の soundID の値を SystemSoundID データ型に変換して渡しています。
AudioServicesPlayAlertSoundWithCompletion(SystemSoundID(kSystemSoundID_Vibrate)) {}
struct MainView: View {
//(プロパティ省略)
var body: some View {
ZStack {
//(省略)
}
.onReceive(timeManager.timer) { _ in
guard self.timeManager.timerStatus == .running else { return }
if self.timeManager.duration > 0 {
//(省略)
} else {
//(省略)
//アラーム音を鳴らす
AudioServicesPlayAlertSoundWithCompletion(self.timeManager.soundID, nil)
//バイブレーションを作動させる
AudioServicesPlayAlertSoundWithCompletion(SystemSoundID(kSystemSoundID_Vibrate)) {}
}
}
}
}
次回は、アラームとバイブレーションのオン/オフなどを含む設定画面を作成していきます。