The timer adjustments its dimension when the time runs out, for instance from 00:52 to 00:51, as a result of the one takes up much less house. How can I forestall the timer from leaping round when it encounters numbers that take up much less house? I wish to repair it within the center in order that it can not transfer. Thanks very a lot.
import SwiftUI
import AVFoundation
struct TimerView: View {
@Binding var length: TimeInterval
@Binding var isEMOTM: Bool // Neues Binding
@State personal var remainingTime: TimeInterval = 0
@State personal var preStartCountdownTime: TimeInterval = 10 // Umbenannte Variable für den initialen Countdown
@State personal var timer: Timer?
@State personal var isRunning: Bool = false
@State personal var isCountdown: Bool = true
@State personal var soundEffect: AVAudioPlayer?
var isCountUp: Bool = false
var isInterval: Bool = false
var isCustomTimer: Bool = false // Neues Flag für den CustomTimer
var highDuration: TimeInterval = 0
var lowDuration: TimeInterval = 0
var numberOfRounds: Int = 0
@State personal var currentRound: Int = 0
@State personal var isHighInterval: Bool = true
@Atmosphere(.presentationMode) var presentationMode
var physique: some View {
GeometryReader { geometry in
let iconSize = min(geometry.dimension.width, geometry.dimension.peak) * 0.15
VStack {
Spacer() // Push timer down
VStack {
Spacer()
Textual content(isCountdown ? "(Int(preStartCountdownTime))" : timeString(remainingTime))
.font(.customized("digital-7", dimension: 150)) // Regulate .padding(.horizontal, 20)
.padding(.vertical, 5)
.background(.black.opacity(0.75))
.clipShape(.capsule)
.fixedSize() // Preserve dimension fixed
Spacer()
}
.body(maxWidth: .infinity)
// Zeigt die aktuelle Runde an, nur wenn es ein Intervall- oder EMOTM-Timer ist
if isInterval || isEMOTM {
HStack {
// Textual content hyperlinks unten ausrichten
Textual content("(currentRound + 1) / (numberOfRounds)")
.foregroundColor(.grey)
}
}
Spacer()
// Konfiguriert die Steuerungsschaltflächen
HStack(spacing: 20) {
Button(motion: {
presentationMode.wrappedValue.dismiss() // Schließt die Ansicht
}) {
Picture(systemName: "arrow.left.circle") // Image für Zurück
.resizable()
.aspectRatio(contentMode: .match)
.foregroundColor(.white)
.padding()
.body(width: iconSize, peak: iconSize)
.background(Shade.grey)
.cornerRadius(10)
}
Button(motion: {
if isRunning {
stopTimer() // Stoppt den Timer
} else {
if isCountdown {
startCountdown() // Startet den Countdown
} else {
startTimer() // Startet den Timer
}
}
}) {
Picture(systemName: isRunning ? "pause.circle" : (isCountdown ? "play.circle" : "play.circle")) // Image für Begin/Pause
.resizable()
.aspectRatio(contentMode: .match)
.foregroundColor(.white)
.padding()
.body(width: iconSize, peak: iconSize)
.background(isRunning ? Shade.orange : Shade.inexperienced)
.cornerRadius(10)
}
Button(motion: {
resetTimer() // Setzt den Timer zurück
}) {
Picture(systemName: "arrow.counterclockwise.circle") // Image für Zurücksetzen
.resizable()
.aspectRatio(contentMode: .match)
.foregroundColor(.white)
.padding()
.body(width: iconSize, peak: iconSize)
.background(Shade.purple)
.cornerRadius(10)
}
}
.padding(.horizontal)
}
.background(Shade(.black)) // Hintergrundfarbe der Ansicht
}
.onAppear {
resetTimer() // Setzt den Timer zurück, wenn die Ansicht erscheint
}
}
// Startet den Countdown-Timer
personal func startCountdown() {
stopTimer() // Stoppt den laufenden Timer
isRunning = true
isCountdown = true
preStartCountdownTime = 2 // Setzt die Countdown-Zeit (später ändern)
playSound(sound: "countdown") // Spielt den Countdown-Sound ab
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in // Erstellt einen wiederholenden Timer
if preStartCountdownTime == 4 {
playSound(sound: "racestart") // Spielt den Begin-Sound ab, wenn 4 Sekunden verbleiben
}
if preStartCountdownTime > 0 {
preStartCountdownTime -= 1 // Reduziert die Countdown-Zeit um 1 Sekunde
print("Countdown: (preStartCountdownTime)")
} else {
isCountdown = false
startTimer() // Startet den Haupt-Timer, wenn der Countdown endet
}
}
}
// Startet den Haupt-Timer
personal func startTimer() {
stopTimer() // Stoppt den laufenden Timer
isRunning = true
playSound(sound: "begin") // Spielt den Begin-Sound ab
if isInterval {
remainingTime = isHighInterval ? highDuration : lowDuration // Setzt die Dauer für Excessive- oder Low-Intervall
} else if isCustomTimer {
remainingTime = 0 // Setzt die Dauer für den CustomTimer
} else if isCountUp {
remainingTime = 0 // Setzt die Dauer für Depend-Up
} else {
remainingTime = length // Setzt die Dauer für Depend-Down
}
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in // Erstellt einen wiederholenden Timer
if isInterval {
if remainingTime > 0 {
remainingTime -= 1 // Reduziert die verbleibende Zeit um 1 Sekunde
print("Timer working: (remainingTime) seconds left")
} else {
if isHighInterval {
isHighInterval.toggle() // Wechselt zu Low-Intervall
remainingTime = lowDuration
print("Switching to low interval: (remainingTime) seconds")
} else {
currentRound += 1 // Erhöht die Rundenzahl nach Excessive- und Low-Intervall
if currentRound >= numberOfRounds {
stopTimer() // Stoppt den Timer, wenn alle Runden abgeschlossen sind
print("Accomplished all rounds")
return
}
isHighInterval.toggle() // Wechselt zu Excessive-Intervall
remainingTime = highDuration
print("Switching to excessive interval: (remainingTime) seconds")
}
}
} else if isCustomTimer {
if remainingTime < length {
remainingTime += 1 // Erhöht die vergangene Zeit um 1 Sekunde
print("Customized Timer working: (remainingTime) seconds handed")
} else {
stopTimer() // Stoppt den Timer, wenn die Dauer erreicht ist
print("Accomplished customized timer")
}
} else if isCountUp {
if remainingTime < length {
remainingTime += 1 // Erhöht die vergangene Zeit um 1 Sekunde
print("Depend-Up Timer working: (remainingTime) seconds handed")
} else {
stopTimer() // Stoppt den Timer, wenn die Dauer erreicht ist
print("Accomplished count-up timer")
}
} else { //Countdown Struktur auch von Emom genutzt
if remainingTime > 0 {
remainingTime -= 1 // Reduziert die verbleibende Zeit um 1 Sekunde
print("Timer working: (remainingTime) seconds left")
} else {
if isEMOTM {
currentRound += 1
if currentRound < numberOfRounds {
remainingTime = length
} else {
stopTimer() // Stoppt den Timer
}
} else {
stopTimer() // Stoppt den Timer, wenn die Zeit abgelaufen ist
print("Timer accomplished")
}
}
}
}
}
// Setzt den Timer zurück
personal func resetTimer() {
stopTimer() // Stoppt den laufenden Timer
if isInterval {
remainingTime = highDuration // Setzt die verbleibende Zeit auf die Excessive-Intervall-Dauer
currentRound = 0 // Setzt die aktuelle Runde auf 0
isHighInterval = true
} else if isCustomTimer {
remainingTime = 0 // Setzt die verbleibende Zeit auf 0 für den CustomTimer
} else if isCountUp {
remainingTime = 0 // Setzt die verbleibende Zeit auf 0 für Depend-Up
} else {
remainingTime = length // Setzt die verbleibende Zeit auf die Dauer für Depend-Down
}
isCountdown = true
preStartCountdownTime = 10 // Setzt den Countdown auf 10 Sekunden
print("Timer reset")
}
// Stoppt den Timer
personal func stopTimer() {
isRunning = false
timer?.invalidate() // Invalidiert den Timer
timer = nil
print("Timer stopped")
}
// Formatiert die verbleibende Zeit als String
personal func timeString(_ interval: TimeInterval) -> String {
let minutes = Int(interval) / 60
let seconds = Int(interval) % 60
return String(format: "%02d:%02d", minutes, seconds) // Gibt die Zeit im Format MM:SS zurück
}
// Spielt einen Soundeffekt ab
personal func playSound(sound: String) {
guard let url = Bundle.predominant.url(forResource: sound, withExtension: "mp3") else {
print("Sound file not discovered: (sound).mp3")
return
}
do {
soundEffect = attempt AVAudioPlayer(contentsOf: url)
soundEffect?.play() // Spielt den Sound ab
print("Taking part in sound: (sound).mp3")
} catch {
print("Error taking part in sound: (error.localizedDescription)")
}
}
}
// Vorschau der TimerView für das SwiftUI Preview
struct TimerView_Previews: PreviewProvider {
static var previews: some View {
TimerView(
length: .fixed(600),
isEMOTM: .fixed(false), // Füge dieses Binding hinzu
isCountUp: false,
isInterval: true,
isCustomTimer: false, // Setze isCustomTimer auf false für die Vorschau
highDuration: 60,
lowDuration: 30,
numberOfRounds: 5 // Konfiguriert die Vorschau der TimerView
)
}
}
Mounted timer place