You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift UI中EMOM间隔计时器整点发声异常问题求助

问题解决:EMOM计时器整点分钟提示音修复

问题分析

当前代码存在两个核心问题:

  1. 独立的soundTimer采用固定60秒重复触发逻辑,暂停恢复后会从暂停时刻重新计算间隔,导致提示音无法落在剩余时间的整点分钟节点(如9:00、8:00这类整分点)。
  2. 计时基础逻辑错误:secondsLeft直接存储分钟数,却在计时器中每秒减1,导致实际计时时长远短于设置值。

解决方案

  1. 修正计时基础逻辑:将设置的分钟数转换为秒数存储,确保计时时长与预期一致。
  2. 替换固定间隔定时器:移除独立的soundTimer,在主计时器的每秒回调中检查剩余时间是否为60的整数倍(即整点分钟节点),满足条件时播放提示音,确保无论暂停恢复多少次,提示音都只在整分点触发。

修改后的完整代码

import Foundation
import SwiftUI
import AVFAudio

// 补充原代码缺失的枚举定义
enum EMOMTimerMode {
    case initial
    case running
    case paused
}

class EmomTimerModes: ObservableObject {
    
    @Published var timerMode: EMOMTimerMode = .initial
    // 存储总秒数,初始值基于用户设置的分钟数转换
    @Published var secondsLeft = UserDefaults.standard.integer(forKey: "timerLength") * 60
    
    var timer = Timer()
    
    func setTimerLength(minutes: Int) {
        let defaults = UserDefaults.standard
        defaults.set(minutes, forKey: "timerLength")
        // 将分钟转换为秒存储,修正计时逻辑
        secondsLeft = minutes * 60
    }
    
    func start() {
        timerMode = .running
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
            // 触发整点分钟提示音:剩余时间为60的整数倍且大于0
            if self.secondsLeft > 0 && self.secondsLeft % 60 == 0 {
                playSound(sound: "race-start", type: "wav")
            }
            
            // 剩余3秒内的提示音逻辑保持不变
            if self.secondsLeft > 0 && self.secondsLeft <= 3 {
                playSound(sound: "race-beep", type: "wav")
            }
            
            if self.secondsLeft == 1 {
                playSound(sound: "race-start", type: "wav")
                self.timer.invalidate()
                self.reset()
            }
            
            guard self.secondsLeft > 0 else {
                self.timer.invalidate()
                return
            }
            self.secondsLeft -= 1
        })
    }

    
    func reset() {
        self.timerMode = .initial
        let savedMinutes = UserDefaults.standard.integer(forKey: "timerLength")
        // 重置时转换为秒数
        self.secondsLeft = savedMinutes * 60
        timer.invalidate()
    }
    
    func pause() {
        self.timerMode = .paused
        timer.invalidate()
    }
}

// 补充原代码缺失的音效播放函数
func playSound(sound: String, type: String) {
    guard let path = Bundle.main.path(forResource: sound, ofType: type) else { return }
    let url = URL(fileURLWithPath: path)
    
    do {
        let player = try AVAudioPlayer(contentsOf: url)
        player.play()
    } catch let error {
        print("播放音效失败:\(error.localizedDescription)")
    }
}

关键修改说明

  • 计时逻辑修正:所有涉及secondsLeft的操作都将分钟转换为秒,确保计时时长与设置一致。
  • 整点提示音触发:通过secondsLeft % 60 == 0判断剩余时间是否为整分点,每次进入整分点时仅触发一次提示音,暂停恢复后,主计时器会自动检测下一个整分点。
  • 移除冗余定时器:删除独立的soundTimer,减少资源占用,同时避免定时器不同步的问题。

内容的提问来源于stack exchange,提问作者SH21

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 02:25:20