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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:45:56