ios – SwiftUI Countdowntimer bouncing (Timer adjustments its dimension when the time runs out)

ios – SwiftUI Countdowntimer bouncing (Timer adjustments its dimension when the time runs out)


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 area. How can I forestall the timer from leaping round when it encounters numbers that take up much less area? I need 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.top) * 0.15
            VStack {
                        Spacer() // Push timer down

                        VStack {
                            Spacer()
                            Textual content(isCountdown ? "(Int(preStartCountdownTime))" : timeString(remainingTime))
                                .font(.customized("digital-7", dimension: 150)) // Modify     .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, top: iconSize)
                            .background(Coloration.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, top: iconSize)
                            .background(isRunning ? Coloration.orange : Coloration.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, top: iconSize)
                            .background(Coloration.purple)
                            .cornerRadius(10)
                    }
                }
                .padding(.horizontal)
            }
            .background(Coloration(.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 Rely-Up
        } else {
            remainingTime = length // Setzt die Dauer für Rely-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("Rely-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 Rely-Up
        } else {
            remainingTime = length // Setzt die verbleibende Zeit auf die Dauer für Rely-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 = strive AVAudioPlayer(contentsOf: url)
            soundEffect?.play() // Spielt den Sound ab
            print("Taking part in sound: (sound).mp3")
        } catch {
            print("Error enjoying 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
        )
    }
}

Fastened timer place

Leave a Reply

Your email address will not be published. Required fields are marked *