我正在尝试使用 this cool timer from kavsoft
但是当我隐藏应用程序或关闭屏幕设备(而不是关闭应用程序)时,此计时器在后台模式下几秒或几分钟后停止计数并停止。如果我打开应用程序,它会再次继续倒计时。
我尝试了原始代码和修改后的代码。在我的中,我制作了小时分钟秒的格式。在其中任何一个中,后台模式下的计数都会停止工作。 有办法解决吗? 我的应用程序可能需要长达 2 小时才能在后台模式下运行。 这是我用 swift 修改的代码
import SwiftUI
import UserNotifications
struct TimerDiscovrView : View {
@State var start = false
@State var to : CGFloat = 0
@State var MaxCount = 0
@State var count = 0
var testTimer: String
var testName: String
@State var time = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View{
ZStack{
//заливка экрана
Color.black.opacity(0.06).edgesIgnoringSafeArea(.all)
VStack{
ZStack{
Circle()
.trim(from: 0, to: 1)
.stroke(Color.black.opacity(0.09), style: StrokeStyle(lineWidth: 35, lineCap: .round))
.frame(width: 280, height: 280)
Circle()
.trim(from: 0, to: self.to)
.stroke(Color.red, style: StrokeStyle(lineWidth: 35, lineCap: .round))
.frame(width: 280, height: 280)
.rotationEffect(.init(degrees: -90))
VStack{
Text("\(valueFormat(mCount: count/3600)):\(valueFormat(mCount: count/60%60)):\(valueFormat(mCount: count%60))")
.font(.system(size: 45))
.fontWeight(.bold)
.padding(.top)
.padding(.bottom)
Text(LocalizedStringKey("Total time")).lineLimit(2)
Text("\(valueFormat(mCount: MaxCount/3600)):\(valueFormat(mCount: MaxCount/60%60)):\(valueFormat(mCount: MaxCount%60))")
.font(.title)
}
}
VStack {
HStack(spacing: 20){
Button(action: {
if self.count == MaxCount {
self.count = 0
withAnimation(.default){
self.to = 0
}
}
self.start.toggle()
}) {
HStack(spacing: 15){
Image(systemName: self.start ? "pause.fill" : "play.fill")
.foregroundColor(.white)
//текст на кнопке
// Text(self.start ? "Pause" : "Play")
// .foregroundColor(.white)
}
.padding(.vertical)
.frame(width: (UIScreen.main.bounds.width / 2) - 55)
.background(Color.red)
.clipShape(Capsule())
.shadow(radius: 6)
}
Button(action: {
self.count = 0
withAnimation(.default){
self.to = 0
}
}) {
HStack(spacing: 15){
Image(systemName: "arrow.clockwise")
.foregroundColor(.red)
//текст на кнопке
// Text("Restart")
// .foregroundColor(.red)
}
.padding(.vertical)
.frame(width: (UIScreen.main.bounds.width / 2) - 55)
.background(
Capsule()
.stroke(Color.red, lineWidth: 2)
)
.shadow(radius: 6)
}
}
Text(LocalizedStringKey("Set timer for")).font(.subheadline).lineLimit(1)
Text("\(testName)").font(.title).lineLimit(2)
VStack {
Text(LocalizedStringKey("Attention")).font(.footnote).foregroundColor(.gray).fixedSize(horizontal: false, vertical: true)
}.padding(.horizontal)
}
.padding(.top)
.padding(.bottom, 30)
}
}.navigationBarTitle(LocalizedStringKey("Set timer"), displayMode: .inline)
.onAppear(perform: {
if self.MaxCount == 0 {
let arrayTimer = testTimer.split(separator: " ")
if arrayTimer.count > 1 {
let counts = Int(arrayTimer[0]) ?? 0
/// Преобразование в секунды
switch arrayTimer[1] {
case "min":
self.MaxCount = counts*60
case "hour":
self.MaxCount = counts*3600
case "hours":
self.MaxCount = counts*3600
default:
self.MaxCount = counts
}
}
}
UNUserNotificationCenter.current().requestAuthorization(options: [.badge,.sound,.alert]) { (_, _) in
}
})
.onReceive(self.time) { (_) in
if self.start{
if self.count != MaxCount {
self.count += 1
print("hello")
withAnimation(.default){
self.to = CGFloat(self.count) / CGFloat(MaxCount)
}
}
else {
self.start.toggle()
self.Notify()
}
}
}
}
func Notify(){
let content = UNMutableNotificationContent()
/// key - ключ_локализованной_фразы, comment не обязательно заполнять
content.title = NSLocalizedString("Perhaps your test is ready", comment: "")
/// с аргументами (key заменяете на нужное)
// Вид локализованной строки в файлах локализации "key %@"="It,s time to check your %@";
content.body = String.localizedStringWithFormat(NSLocalizedString("It's time to check your %@", comment: ""), self.testName)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let req = UNNotificationRequest(identifier: "MSG", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(req, withCompletionHandler: nil)
}
func valueFormat(mCount: Int) -> String {
String(format: "%02d", arguments: [mCount])
}
}
最佳答案
我以前遇到过这个问题,我一直在努力解决它,但是我没有得到任何有用的答案。我尝试激活后台模式
但没有成功。
这是我如何克服这个问题的:
假设您的计数器已经开始计数,计数器现在处于5秒
,然后用户突然进入后台,我们需要做什么?
让我们转到
SceneDelegate
并声明这些变量var appIsDeadAt: Double? var appIsBackAliveAt: Double?
我们需要在
sceneDidEnterBackground
中保存用户进入后台的时间appIsDeadAt = Date().timeIntervalSince1970
当用户再次进入应用程序时,我们需要计算应用程序在后台停留的时间。转到
sceneWillEnterForeground
并获取应用程序恢复事件的时间appIsBackAliveAt = Date().timeIntervalSince1970
现在我们需要计算应用程序在后台停留的总时间
let finalTime = (appIsBackAliveAt! - appIsDeadAt!)
最后,假设应用程序在后台停留了
10 秒
,而之前的时间是5
,即5 + FinalTime
> (即 10 秒)总时间将为15 秒
,然后更新计数器时间以从15 秒
继续计数。
注意:使用UserDefaults
将值传递给您的计数器,以方便您。
一个实时示例来 self 自己的应用程序,该应用程序在 Applestore 上发布:Chatiw
我的计时器的基本原理是,当用户观看广告视频时,我会奖励他 300 秒无广告的时间。
完成上述所有步骤后,当我有最后一次时间时,我会将其存储在 UserDefaults
上:
userDefaults.setValue(finalTime.rounded(), forKey: "timeInBg")
然后在包含计数器的主代码上,我将计数器更新为新时间:
adsRemovalCounter = adsRemovalCounter - Int(self.userDefaults.double(forKey: "timeInBg"))
之后我将删除finalTime
的UserDefaults
键,这样当用户再次进入后台时它就不会干扰下一步的计算。
self.userDefaults.removeObject(forKey: "timeInBg")
这是我刚刚在 SwiftUI
中制作的一个示例:
ContentView
文件:
import SwiftUI
struct ContentView: View {
@ObservedObject var counterService = CounterService()
var body: some View {
VStack {
Text("\(self.counterService.counterTime)")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color.white)
.frame(width: 100, height: 80)
.background(Color.red)
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
.shadow(color: Color.red.opacity(0.5), radius: 10, x: 5, y: 2)
.padding()
.padding(.bottom, 100)
Button(action: {
self.counterService.startCounting()
}) {
Text("Start Counter")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.white)
.frame(width: 200, height: 80)
.background(Color.gray)
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
.shadow(color: Color.black.opacity(0.5), radius: 10, x: 5, y: 2)
}
}
}
}
CounterService
文件:
import SwiftUI
class CounterService: ObservableObject {
@Published var counterTime: Int = 0
func startCounting(){
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
if UserDefaults.standard.string(forKey: "timeInBg") != nil {
self.counterTime = Int(UserDefaults.standard.double(forKey: "timeInBg")) + self.counterTime
UserDefaults.standard.removeObject(forKey: "timeInBg")
}
self.counterTime += 1
}
}
}
SceneDelegate
文件:
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let userDefaults = UserDefaults.standard
var appIsDeadAt: Double?
var appIsBackAliveAt: Double?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = ContentView().environment(\.managedObjectContext, context)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
appIsBackAliveAt = Date().timeIntervalSince1970
if appIsDeadAt != nil && appIsBackAliveAt != nil {
let finalTime = (appIsBackAliveAt! - appIsDeadAt!)
userDefaults.setValue(finalTime.rounded(), forKey: "timeInBg")
}
}
func sceneDidEnterBackground(_ scene: UIScene) {
appIsDeadAt = Date().timeIntervalSince1970
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}
}
瞧!结果如下:
我希望这能回答您的问题。
关于ios - 当用户隐藏应用程序或关闭设备时,swiftui 中的自定义计时器停止在后台获取中计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65341931/