I have tried a couple different approaches to running a timer (onRecieve and scheduled) across three different applications within Xcode SwiftUI and have yet to see the timer work in the simulator. I followed the instructions from the following video for demonstration purposes:
https://www.youtube.com/watch?v=y68YmyTB7JU
I'm not sure if Xcode has a setting that needs to be modified to enable the timer in the simulator on my laptop or if I am missing something needed for a more recent version of Xcode/SwiftUI. This video is only about 6 months old as of this post though.
Thank you in advance!
Content View:
import SwiftUI
struct ContentView: View {
@ObservedObject var stopWatchManager = StopWatchManager()
var body: some View {
VStack {
Text(String(format: "%.1f", stopWatchManager.secondsElapsed))
.font(.system(size: 50))
.foregroundColor(.yellow)
.padding(.top, 200)
.padding(.bottom, 100)
.padding(.trailing, 100)
.padding(.leading, 100)
if stopWatchManager.mode == .stopped{
Button(action: {self.stopWatchManager.start()}){
TimerButton(label: "Start", buttonColor: .green, textColor: .black)
}
}
if stopWatchManager.mode == .running{
Button(action: {self.stopWatchManager.pause()}){
TimerButton(label: "Pause", buttonColor: .green, textColor: .black)
}
}
if stopWatchManager.mode == .paused{
Button(action: {self.stopWatchManager.start()}){
TimerButton(label: "Start", buttonColor: .green, textColor: .black)
}
Button(action: {self.stopWatchManager.stop()}){
TimerButton(label: "Stop", buttonColor: .red, textColor: .black)
}
.padding(.top, 30)
}
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct TimerButton: View {
let label: String
let buttonColor: Color
let textColor: Color
var body: some View {
Text(label)
.foregroundColor(textColor)
.padding(.vertical, 20)
.padding(.horizontal, 90)
.background(buttonColor)
.cornerRadius(10)
}
}
StopWatchManager:
import Foundation
import SwiftUI
class StopWatchManager: ObservableObject{
enum stopWatchMode {
case running
case stopped
case paused
}
@Published var mode: stopWatchMode = .stopped
@Published var secondsElapsed = 0
var timer = Timer()
func start() {
mode = .running
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
timer in self.secondsElapsed = 1
}
}
func pause() {
timer.invalidate()
mode = .paused
}
func stop() {
timer.invalidate()
secondsElapsed = 0
mode = .stopped
}
}
CodePudding user response:
This is a sneaky bug that's hard to recognize.
In your code, you've defined elapsedSeconds
as:
@Published var secondsElapsed = 0
Swift does type inference to infer that secondsElapsed
is an Int
here. When you use String(format: "%.1f"...)
it expects a Double
(floating point number) and doesn't update properly with an Int
.
In the video code, you'll see that secondsElapsed
is defined as:
@Published var secondsElapsed = 0.0
Swift infers this to be a Double
-- if you make that change, everything will work as expected.
PS: You can look into using Timer with Combine (eg Timer.publish
) for a more SwiftUI-centric way of doing things. See the documentation