Reputation: 73
I created a timer. When the timer ends it should play a complete sounds once. It is a soundfile that is about 5 seconds long.
But what happens is that when the timer ends, the sounds is playing over and over again and it only plays the first 2 seconds of the sound.
It happens on the simulator and a real device.
I have no idea why this is happening. I've been googling voor quite some time but I can't find a solution.
I tried to connect the sound to buttons, and the play and stop functions work fine. But even if I try to put the 'SoundManager.instance.stopSound()' into the stop button, it still doesn't stop playing the sound when I tap it when the timer ends.
Can someone tell me what I am doing wrong?
This is my code:
import SwiftUI
import AVKit
class SoundManager {
static let instance = SoundManager()
var player: AVAudioPlayer?
func playSound() {
guard let url = Bundle.main.url(forResource: "Tada", withExtension: ".mp3") else {return}
do {
player = try AVAudioPlayer(contentsOf: url)
player?.play()
} catch let error {
print("Error playing sound. \(error.localizedDescription)")
}
}
func stopSound() {
// Stop AVAudioPlayer
player?.stop()
}
}
struct TimerCountDown: View {
@State var countDownTimer = 30
@State var timerRunning = false
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
Text("\(countDownTimer)")
.onReceive(timer) { _ in
if countDownTimer > 0 && timerRunning {
countDownTimer -= 1
}
else if countDownTimer < 1 {
SoundManager.instance.playSound()
}
else {
timerRunning = false
}
}
.font(.system(size: 80, weight: .bold))
.opacity(0.80)
HStack (spacing: 30) {
Button("Start") {
timerRunning = true
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
Button("Stop") {
timerRunning = false
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
Button("Reset") {
countDownTimer = 30
}
.padding()
.background(.red)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
}
struct TimerCountDown_Previews: PreviewProvider {
static var previews: some View {
TimerCountDown()
}
}
Upvotes: 0
Views: 435
Reputation: 154631
Your problem is in your .onReceive(timer)
code. When the countDownTimer
has reached 0
, the timer is still publishing every second, so your code enters the second clause which plays the end timer sound. It does this every second. You should only do that if timerRunning
and you should set that to false
when your count reaches 0
.onReceive(timer) { _ in
if timerRunning {
if countDownTimer > 0 {
countDownTimer -= 1
if countDownTimer == 0 {
timerRunning = false
SoundManager.instance.playSound()
}
}
}
}
Note: Your code leaves the timer publishing at all times, and it is really only needed when the timer is running. Check this answer for ideas on how to stop and restart the publishing of the timer.
Upvotes: 1