Вопрос или проблема
Я работаю над функцией Помодоро, и моя единственная проблема в том, что когда таймер работает, iPhone или iPad отключаются, потому что вы не используете устройство. Как я могу изменить это в своем коде, чтобы оно не отключалось, пока таймер активен, или могу ли я использовать переключатель, чтобы решить для себя, хочу ли я этого или нет?
Заранее большое спасибо за вашу помощь
Сначала я пытался активировать фоновую обработку в XCode и думал, что это будет автоматически сконвертировано, но, к сожалению, это не помогло.
Вот мой код:
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
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()
}
}
.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
)
.presentationBackground(.clear)
}
}
.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("Общее рабочее время: \(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
})
}
}
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 = "Таймер Помодоро"
content.body = NSLocalizedString(mode == "work" ? "Время снова работать!" : "Время для перерыва!", comment: "Уведомление таймера Помодоро")
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 .
В зависимости от того, как вы настроили свое приложение:
var body: some View {
Text("Привет")
.onAppear { UIApplication.shared.isIdleTimerDisabled = true }
.onDisappear { UIApplication.shared.isIdleTimerDisabled = false }
}
Или я добавил это в AppDelegate в SwiftUI.
Просто не забудьте установить его в false
, как только закончите с ним.
Ответ или решение
Как отключить автоматическое отключение экрана на iOS в SwiftUI
Когда вы работаете с приложением Pomodoro в SwiftUI, может возникнуть проблема, когда экран вашего устройства (iPhone или iPad) выключается, поскольку вы не взаимодействуете с ним в течение определенного времени. Это может быть особенно неудобно во время работы с таймером. Однако существует способ предотвратить эту автоматическую блокировку экрана, используя свойство isIdleTimerDisabled
из UIKit. В данном ответе мы рассмотрим, как можно интегрировать это решение в ваше приложение.
Использование свойства isIdleTimerDisabled
Свойство UIApplication.shared.isIdleTimerDisabled
позволяет вам управлять тем, будет ли экран вашего устройства выключаться или нет. Когда вы устанавливаете это свойство в true
, система не будет автоматически отключать экран. Мы рекомендуем использовать этот флаг в зависимости от состояния таймера в вашем приложении.
Пример кода
Чтобы добавить это в ваше приложение Pomodoro, вы можете сделать следующее:
- Внутри вашего
FocusView
, добавьте код для установкиisIdleTimerDisabled
вtrue
при запуске таймера и установите его вfalse
при его завершении.
Вот пример, как это можно реализовать:
import SwiftUI
struct FocusView: View {
// ... ваши существующие свойства ...
var body: some View {
ZStack {
// ... остальной код вашего интерфейса ...
.onAppear {
UIApplication.shared.isIdleTimerDisabled = true // Отключаем автовыключение экрана
}
.onDisappear {
UIApplication.shared.isIdleTimerDisabled = false // Включаем автовыключение экрана, когда вид исчезает
}
}
// ... остальной код вашего представления ...
}
// ... остальные функции вашего View ...
}
Завершение таймера
Важно помнить, что вы должны возвращать значение isIdleTimerDisabled
к false
, когда таймер завершен или когда ваше представление больше не активно. Например, в вашем методе completeSession()
, где вы завершаете сессию, можно добавить код для восстановления значения:
private func completeSession() {
isRunning = false
showCompletionSheet = true
UIApplication.shared.isIdleTimerDisabled = false // Включаем автовыключение экрана
}
Пользовательский интерфейс для выбора
Если вы хотите предоставить пользователю возможность выбора, хочет ли он отключить автоматическое отключение экрана, вы можете добавить переключатель (Switch) в ваш интерфейс. Пример кода для реализации переключателя:
@State private var disableIdleTimer: Bool = false
var body: some View {
VStack {
Toggle("Отключить автоматическое отключение экрана", isOn: $disableIdleTimer)
.onChange(of: disableIdleTimer) { value in
UIApplication.shared.isIdleTimerDisabled = value
}
// Остальной код…
}
}
Заключение
Использование свойства isIdleTimerDisabled
позволяет вам контролировать поведение экрана вашего устройства во время работы с таймером. Правильное управление этим свойством улучшит пользовательский опыт, позволяя пользователям сосредоточиться на своих задачах без необходимости постоянно взаимодействовать с устройством.
Не забудьте протестировать ваше приложение, чтобы убедиться, что экран отключается обратно, когда это необходимо, и обеспечьте пользователю возможность выбора. Это не только улучшит функциональность, но и повысит общее восприятие вашего приложения.