Reputation: 427
I've got a countdown timer that counts down and executes the code twice but then it doesn't reset, instead it continues counting in negative numbers. Can someone tell me why?
var didCount = 4
func startDelayTimer() {
delayTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
self.startDelayCount()
})
}
func startDelayCount() {
delayTime -= 1
timeLbl.text = String(delayTime)
if delayTime <= 3 {
soundPLayer.play()
}
if delayTime == 0 {
delayTimer.invalidate()
doSomething()
}
}
func doSomething() {
doCount += 1
if doCount < didCount {
startDelayTimer()
}
else {
print("done")
}
}
Upvotes: 0
Views: 291
Reputation: 63272
The direct issue is that you reset the timer without remebering to reset delayTime
.
But I think there's also an architectural issue, in that you have a murky mix of responsibilities (managing a timer, updating a label, and playing sounds). I'd suggest you extract the timer responsibilities elsewhere.
Perhaps something along these lines:
/// A timer which counts from `initialCount` down to 0, firing the didFire callback on every count
/// After each full countdown, it repeats itself until the repeatLimit is reached.
class RepeatingCountDownTimer {
typealias FiredCallback: () -> Void
typealias FinishedCallback: () -> Void
private var initialCount: Int
private var currentCount: Int // Renamed from old "delayTime"
private var repeatCount = 0 // Renamed from old "doCount"
private let repeatLimit: Int // Renamed from old "didCount"
private var timer: Timer?
private let didFire: FiredCallback
private let didFinish: FinishedCallback
init(
countDownFrom initialCount: Int,
repeatLimit: Int,
didFire: @escaping FiredCallback,
didFinish: @escaping FinishedCallback
) {
self.initialCount = initialCount
self.currentCount = initialCount
self.repeatLimit = repeatLimit
self.didFire = didFire
self.didFinish = didFinish
}
public func start() {
self.currentCount = self.initialCount
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] in
self?.fire()
})
}
private func fire() {
currentCount -= 1
self.didFire(currentCount)
if currentCount == 0 {
repeat()
}
}
private func repeat() {
repeatCount += 1
if repeatCount < repeatLimit {
self.timer?.invalidate()
start()
} else {
finished()
}
}
private func finished() {
self.timer?.invalidate()
self.timer = nil
self.didFinish()
}
}
That's just rough psuedo-code, which will certainly need tweaking. But the idea is to separate timer and state management from the other things you need to do. This should make it easier to debug/develop/test this code, replacing useless names like doSomething
with more concretely named events.
The usage might look something like:
let countDownTimer = RepeatingCountDownTimer(
countDownFrom: 4,
repeatLimit: 4,
didFire: { count in
timeLbl.text = String(count)
soundPlayer.play()
},
didFinish: { print("done") }
)
countDownTimer.start()
Upvotes: 1