SwiftUIでSliderを曲の再生位置に同期させる方法を説明する。
Swift 5.7 / Xcode 14.0 / iOS 16.0
結論
ユーザーがSliderを操作したときに曲の再生位置を変更するには、SliderのonEditingChanged内に曲の再生位置を制御する処理を記述する。
曲が進むのに合わせてSliderの位置を追従させるには、Sliderの.onReceive内に曲の再生位置を定期的に取得する処理を記述する。
具体例
以下のAppを作成する。
- Sliderと曲の再生位置が同期するようにする。
- ユーザーがSliderを操作したときに曲の再生位置を変更するために、SliderのonEditingChanged内に曲の再生位置を制御する処理を記述する。
- 曲が進むのに合わせてSliderの位置を追従させるために、Sliderの.onReceive内に曲の再生位置を定期的に取得する処理を記述する。
- 1でSliderと曲の再生位置を同期できるように、曲の再生位置を示す変数には@Publishedを付ける。
- 3で曲の再生位置を定期的に取得できるように、Timerを記述する。
- 再生をタップしたときにViewが更新されないとなぜかTimerが効かないようなので、再生をタップしたときにTextの表示内容が変わるようにする。
import SwiftUI
import AVFoundation
struct ContentView: View {
@ObservedObject var オーディオインスタンス = オーディオクラス()
@State private var 再生or停止: String = "▶ 再生"
var body: some View {
Button(action: {
if 再生or停止 == "▶ 再生" {
再生or停止 = "■ 停止"
オーディオインスタンス.再生()
オーディオインスタンス.タイマー開始()
} else {
再生or停止 = "▶ 再生"
オーディオインスタンス.停止()
}
}) {
Text(String(再生or停止)) // ? 6
}
Slider(value: $オーディオインスタンス.再生位置, // ? 1
in: TimeInterval(0.0)...オーディオインスタンス.曲の長さ,
onEditingChanged: { _ in
オーディオインスタンス.再生位置を変更() // ? 2
})
.onReceive(オーディオインスタンス.timer) { _ in // ? 3
if オーディオインスタンス.isPlaying {
オーディオインスタンス.再生位置 = オーディオインスタンス.audioPlayer!.currentTime
} else {
オーディオインスタンス.タイマー停止()
}
}
.padding()
}
}
class オーディオクラス: ObservableObject {
var isPlaying = false
var audioPlayer: AVAudioPlayer?
var 曲の長さ: TimeInterval
@Published var 再生位置: TimeInterval = 0.0 // ? 4
var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
init() {
let path = Bundle.main.path(forResource: "sample", ofType: "mp3")!
audioPlayer = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
曲の長さ = audioPlayer!.duration
}
func 再生() {
audioPlayer?.play()
isPlaying = true
}
func 停止() {
audioPlayer?.pause()
isPlaying = false
}
func 再生位置を変更() {
audioPlayer?.currentTime = 再生位置
}
func タイマー開始() {
timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() // ? 5
}
func タイマー停止() {
timer.upstream.connect().cancel()
}
}
まとめ
SwiftUIでSliderを曲の再生位置に同期させる方法を説明した。
コメント