SwiftUI番茄钟:如何设置防止设备锁屏及添加开关控制
解决番茄钟运行时设备自动锁屏问题
核心思路
iOS中控制设备是否自动锁屏,需通过UIKit的UIApplication.shared.isIdleTimerDisabled属性实现。我们可以在计时器运行时开启该属性,停止时关闭;同时添加开关让用户自主选择是否启用该功能。
具体实现步骤
1. 导入UIKit框架
在FocusView所在文件顶部添加:
import UIKit
2. 添加用户设置变量
在FocusView结构体中添加持久化存储变量,保存用户是否开启“禁止锁屏”的选择:
@AppStorage("disableIdleTimer") private var disableIdleTimer = true
3. 监听状态变化,自动控制锁屏
在FocusView的body中添加状态监听逻辑,根据计时器运行状态和用户设置自动控制锁屏:
// 监听计时器启停状态 .onChange(of: isRunning) { newValue in UIApplication.shared.isIdleTimerDisabled = newValue && disableIdleTimer } // 监听用户设置变更 .onChange(of: disableIdleTimer) { newValue in if isRunning { UIApplication.shared.isIdleTimerDisabled = newValue } } // 处理应用前后台切换,避免后台持续唤醒设备 .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in UIApplication.shared.isIdleTimerDisabled = false } .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in UIApplication.shared.isIdleTimerDisabled = isRunning && disableIdleTimer }
4. 在设置界面添加开关
修改TimerView,添加绑定到disableIdleTimer的开关,让用户自主选择:
- 更新
TimerView参数:
struct TimerView: View { // 原有参数保留... @Binding var disableIdleTimer: Bool var body: some View { // 原有界面内容保留... Toggle(isOn: $disableIdleTimer) { Text("计时器运行时禁止锁屏") } .padding() } }
- 在
FocusView的sheet中传递绑定:
.sheet(isPresented: $isTimerViewPresented) { TimerView( // 原有参数传递保留... disableIdleTimer: $disableIdleTimer ) .presentationBackground(.clear) }
修改后的完整FocusView代码片段
import SwiftUI import UIKit // 新增导入 struct FocusView: View { @State var progress: Double = 0 @State var isTimerViewPresented: Bool = false @AppStorage("focusTime") private var focusTime: Double = 0 @State private var workDuration: Double = 25 @State private var pauseDuration: Double = 5 @AppStorage("modus") private var modus: String = "work" @State var isRunning: Bool = false @State var pausenint: Int = 2 @State var wiederholungen_Int: Int = 0 @Binding var repetitions: Double @AppStorage("totalWorkTime") private var totalWorkTime: Double = 0 @AppStorage("disableIdleTimer") private var disableIdleTimer = true // 新增用户设置变量 let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() @State var isCompleteWorkTimeShow: Bool = false @State private var showCompletionSheet: Bool = false @State private var totalFocusTime: TimeInterval = 0 func convertSecondsToTime(timeInSeconds: Int) -> String { let minutes = timeInSeconds / 60 let seconds = timeInSeconds % 60 return String(format: "%02i:%02i", minutes, seconds) } var body: some View { ZStack { VStack { Spacer() VStack { ZStack { CircularProgressView_pomodoro(progress: $progress, modus: modus) Text(convertSecondsToTime(timeInSeconds: Int(focusTime))) .font(.custom("ArialRoundedMTBold", size: 70)) .onTapGesture { withAnimation { isTimerViewPresented.toggle() } } } .frame(width: 250, height: 250) } Spacer() HStack { Spacer() Button(action: resetTimer) { Image(systemName: "backward") .font(.system(size: 42)) .foregroundColor(.primary) } .padding() Spacer() Button(action: toggleTimer) { Image(systemName: isRunning ? "pause" : "play") .font(.system(size: 42)) .foregroundColor(.primary) } .padding() Spacer() Button(action: skipTimer) { Image(systemName: "forward") .font(.system(size: 42)) .foregroundColor(.primary) } .padding() Spacer() } if isCompleteWorkTimeShow { HStack { Text("Gesamte Arbeitszeit: \(convertSecondsToTime(timeInSeconds: Int(totalWorkTime)))") .font(.headline) .foregroundColor(.primary) Image(systemName: "arrow.clockwise") .foregroundColor(.primary) .onTapGesture { totalWorkTime = 0 } .padding() } } Spacer() } } .onReceive(timer) { _ in updateTimer() } .sheet(isPresented: $showCompletionSheet) { CompletionView(totalFocusTime: totalFocusTime, onDismiss: { showCompletionSheet = false }) } // 新增状态监听逻辑 .onChange(of: isRunning) { newValue in UIApplication.shared.isIdleTimerDisabled = newValue && disableIdleTimer } .onChange(of: disableIdleTimer) { newValue in if isRunning { UIApplication.shared.isIdleTimerDisabled = newValue } } .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in UIApplication.shared.isIdleTimerDisabled = false } .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in UIApplication.shared.isIdleTimerDisabled = isRunning && disableIdleTimer } .sheet(isPresented: $isTimerViewPresented) { TimerView( progress: $progress, isTimerViewPresented: $isTimerViewPresented, sliderValue: $workDuration, sliderValue_pause: $pauseDuration, sliderValue_wiederholungen: $repetitions, focusTime: $focusTime, modus: $modus, pause: $pausenint, wiederholungen: $wiederholungen_Int, disableIdleTimer: $disableIdleTimer // 新增绑定传递 ) .presentationBackground(.clear) } } private func resetTimer() { focusTime = modus == "work" ? workDuration * 60 : pauseDuration * 60 progress = 0 isRunning = false } private func toggleTimer() { isRunning.toggle() } private func skipTimer() { focusTime = 0 updateTimer() } private func updateTimer() { if isRunning && repetitions > 0 { if focusTime > 0 { focusTime -= 1 if modus == "work" { totalWorkTime += 1 totalFocusTime += 1 } progress = 1.0 - (focusTime / (modus == "work" ? workDuration * 60 : pauseDuration * 60)) } else { switchMode() } } } private func switchMode() { if modus == "work" { modus = "pause" focusTime = pauseDuration * 60 scheduleNotification(for: modus) } else { modus = "work" focusTime = workDuration * 60 repetitions -= 1 if repetitions > 0 { scheduleNotification(for: modus) } else { completeSession() } } progress = 0 } private func completeSession() { isRunning = false showCompletionSheet = true } private func scheduleNotification(for mode: String) { let content = UNMutableNotificationContent() content.title = "Pomodoro Timer" content.body = NSLocalizedString(mode == "work" ? "Zeit, wieder an die Arbeit zu gehen!" : "Zeit für eine Pause!", comment: "Pomodoro timer notification") content.sound = UNNotificationSound.default let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) let request = UNNotificationRequest(identifier: "PomodoroTimer", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) } }
关键说明
isIdleTimerDisabled设为true时,设备不会因闲置自动锁屏;设为false时恢复默认行为。- 使用
@AppStorage保存用户选择,确保重启应用后设置依然有效。 - 监听应用前后台切换,避免应用在后台时仍保持设备唤醒,节省电量。
内容的提问来源于stack exchange,提问作者Johannes Klopp
相关产品推荐
相关产品推荐

