I have a timer program that works. It includes code that sets an environment boolean to true or false based on whether the timers window is the key window or not. What I would like to do is pause the timer when the boolean is false and restart it from where it was paused when the boolean is true. The print statement above the button always prints out the current value (true or false). The print statement inside the timer closure always prints true. I think if I could bind/tie the environment variable isKeyWindow to a @State variable I could solve my problem but am unable to figure out how to do so. Or maybe. there is a better way. Below is my code.
struct ContentView: View {
@Environment(\.isKeyWindow) var isKeyWindow: Bool
@State private var timerCount: Int = 0
@State private var trimProgress: CGFloat = 0.0
@State private var percentProgress: CGFloat = 0.0
var body: some View {
ZStack {
Rectangle()
.fill(Color.clear)
.frame(width: 600, height: 400)
Circle()
.stroke(lineWidth: 15.0)
.opacity(0.2)
.foregroundColor(Color.gray)
.frame(width: 200, height: 200)
.rotationEffect(Angle(degrees: 270.0))
Text(String(format: "%.0f%%", min(self.percentProgress,1) * 100))
.font(.system(size: 50))
Circle()
.trim(from: 0, to: self.trimProgress)
.stroke(style: StrokeStyle(lineWidth: 15.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.blue)
.frame(width: 200, height: 200)
.rotationEffect(Angle(degrees: 270.0))
.animation(Animation.easeInOut)
let _ = print("\(isKeyWindow)")
Button("Download JSON Files") {
Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
let _ = print("In timer \(isKeyWindow)")
self.timerCount += 1
if self.timerCount <= 20 {
self.trimProgress += 0.05
}
if self.timerCount >= 2 {
self.percentProgress += 0.05
}
if self.timerCount > 20 {
timer.invalidate()
}
} // end timer
}.offset(y: 150) // end button
}.padding() // end zstack
} // end var body
} // end content view
Peter, once again you are on the money. I added onChange and in addition to get the functionality I wanted I removed the button and went to a different timer. Instead of pausing the timer I just ignore it when the timer window is not the key window. One odd thing. To make things work properly I had to set keyWindow to false when isKeyWindow is true in the onChange logic. I would have thought you would set keyWindow to true when isKeyWindow is true.
Below is my updated code.
Thanks again.
struct ContentView: View {
@Environment(\.isKeyWindow) var isKeyWindow: Bool
@State private var timerCount: Int = 0
@State private var trimProgress: CGFloat = 0.0
@State private var percentProgress: CGFloat = 0.0
@State private var keyWindow: Bool = true
let timer = Timer.publish(every: 0.25, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
Rectangle()
.fill(Color.clear)
.frame(width: 600, height: 400)
Circle()
.stroke(lineWidth: 15.0)
.opacity(0.2)
.foregroundColor(Color.gray)
.frame(width: 200, height: 200)
.rotationEffect(Angle(degrees: 270.0))
Text(String(format: "%.0f%%", min(self.percentProgress,1) * 100))
.font(.system(size: 50))
Circle()
.trim(from: 0, to: self.trimProgress)
.stroke(style: StrokeStyle(lineWidth: 15.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.blue)
.frame(width: 200, height: 200)
.rotationEffect(Angle(degrees: 270.0))
.animation(Animation.easeInOut)
.onChange(of: isKeyWindow) { _ in
if isKeyWindow == true {
keyWindow = false
} else {
keyWindow = true
}
} // end on change
.onReceive(timer) { time in
if keyWindow == true {
self.timerCount += 1
if self.timerCount <= 20 {
self.trimProgress += 0.05
}
if self.timerCount >= 2 {
self.percentProgress += 0.05
}
if self.timerCount > 20 {
self.timer.upstream.connect().cancel()
}
} // end of main if &&
} // end of on receive
} .padding() // end zstack
} // end var body
} // end content view
This just makes no sense. Remove this code. If @Environment(\.isKeyWindow) is true when it should be false and vice versa, then change the code for the environment value. You need to post the code for this custom environment value.
I tested the your code and it works just fine without the onChange modifier. In the .onReceive(timer) closure, use isKeyWindow instead and get rid of the keyWindow property entirely.