Reputation: 43
I'm working currently on a timer app for the Apple Watch. I'm managed to get a sound when the timer reaches 5 seconds. Only, when I'm pressing "done" the timer sounds still plays and I don't now how to stop the sound. I'm using WKInterfaceDevice.current().play(.success).
I want to stop the sound when the "cancel" button is pressed within the 5 seconds and when the "done" button is pressed.
I cannot find anything on the internet. I think that WKInterfaceDevice does not have an stop function.
struct softView: View {
@State var timerVal = 10
var body: some View {
VStack {
if timerVal > 0 {
Text("Time Remaining")
.font(.system(size: 14))
Text("\(timerVal)")
.font(.system(size: 40))
.onAppear(){
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if self.timerVal > 0 {
self.timerVal -= 1
}
if self.timerVal < 6 {
WKInterfaceDevice.current().play(.success)
}
}
}
Text("Seconds")
.font(.system(size: 14))
Divider()
Spacer()
NavigationLink(destination: ContentView(), label: {Text("Cancel")})
//.cornerRadius(20)
.foregroundColor(Color.red)
.background(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.red, lineWidth: 2)
)
}
else {
NavigationLink(destination: ContentView(), label: {Text("Done")})
.foregroundColor(Color.green)
.background(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.green, lineWidth: 2)
)
}
} .navigationBarHidden(true)
}
Upvotes: 1
Views: 1260
Reputation: 20274
When does your timer
know when to stop?
You have to define the event when the timer is to be stopped. That's where.invalidate
will come handy.
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
guard let _weakSelf = self else { timer.invalidate(); return }
_weakSelf.timerVal -= 1
if _weakSelf.timerVal < 0 { //if timer count goes negative then stop timer
timer.invalidate()
} else if _weakSelf.timerVal < 6 {
WKInterfaceDevice.current().play(.success)
}
}
For more control, i.e. If you want to stop the timer
from, say, a button tap then we will have to make this timer
object global.
Furthermore, if you want to pop the View
after the timer
is completed/cancelled then we need to make more changes.
This all gets a bit complicated but it's simple to understand.
I would suggest you to break out your timer related logic into an ObservableObject
class and use it within your View
.
struct ContentView: View {
@State var isShowingTimer: Bool = false
var body: some View {
NavigationView {
NavigationLink(destination: TimerView(isShowing: $isShowingTimer),
isActive: $isShowingTimer) {
Text("Start Timer")
}
}
}
}
isShowingTimer
controls the push/pop event for TimerView
TimerView
so it can be updated from inside TimerView
in order to pop.struct TimerView: View {
//Trigger for popping this view
@Binding var isShowing: Bool
@ObservedObject var timerControl = TimerControl()
var body: some View {
VStack {
Text("\(timerControl.count)")
.onAppear(perform: {
//start timer event
self.timerControl.startTimer(from: 10)
})
.onDisappear(perform: {
//stop timer if user taps on `Back` from the Navigation Bar
self.timerControl.stopTimer()
})
.onReceive(timerControl.$isComplete, //observe timer completion trigger
perform: { (success) in
//hide this view
self.isShowing = !success
})
Text("Cancel")
.onTapGesture(perform: {
//stop timer event
self.timerControl.stopTimer()
})
}
}
}
class TimerControl: ObservableObject {
@Published var count: Int = 0
@Published var isComplete: Bool = false
private var timer: Timer?
init(){}
func startTimer(from count: Int) {
self.count = count
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] (timer) in
guard let _weakSelf = self else { timer.invalidate(); return }
print(_weakSelf.count)
_weakSelf.count -= 1
if _weakSelf.count <= 0 {
_weakSelf.stopTimer()
} else if _weakSelf.count < 6 {
print(">>make some noise here<<")
}
}
}
func stopTimer() {
guard isComplete == false else { return }
timer?.invalidate()
isComplete = true
}
}
ObservableObject
class can emit changes@Published
variables emit signals of change@ObservedObject
listens for changes on the ObservableObject
.onReceive
handles Publisher
events. In this case listens for changes by timerControl.$isComplete
Upvotes: 1