我在堆栈或其他地方在后台处理NSTimer时遇到了很多问题。我尝试了所有实际上有意义的选项之一,以便在应用程序进入后台时停止计时器
NSNotificationCenter.defaultCenter().addObserver(self, selector: "appDidEnterBackground", name: UIApplicationDidEnterBackgroundNotification, object: nil)
和
NSNotificationCenter.defaultCenter().addObserver(self, selector: "appDidBecomeActive", name: UIApplicationWillEnterForegroundNotification, object: nil)
起初我以为我的问题解决了,我只是保存了应用程序进入后台的时间,并计算了应用程序进入前景时的时间..但后来我注意到时间实际上延迟了3、4、5秒。
..实际上不一样..我已将其与另一台设备上的秒表进行了比较。
在后台运行NSTimer真的有任何SOLID解决方案吗?
您不应该根据进入后台或恢复的时间来进行任何调整,而只需节省您要数的时间(取决于您是在递增还是递减)。然后,当应用再次启动时,您只需在重构计时器时使用该时间即可。
同样,请确保您的计时器处理程序不依赖于调用处理选择器的确切时间(例如, 不要
执行seconds++
任何类似操作,因为您可能希望调用时不会准确地调用它),但请务必返回从/到时间。
这是一个倒数计时器的示例,它说明我们不对任何东西进行“计数”。我们也不在乎介于appDidEnterBackground
和之间的时间appDidBecomeActive
。只需保存停止时间,然后计时器处理程序就可以将目标stopTime
时间与当前时间进行比较,并根据需要显示经过的时间。
例如:
import UIKit
import UserNotifications
private let stopTimeKey = "stopTimeKey"
class ViewController: UIViewController {
@IBOutlet weak var datePicker: UIDatePicker!
@IBOutlet weak var timerLabel: UILabel!
private weak var timer: Timer?
private var stopTime: Date?
let dateComponentsFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
registerForLocalNotifications()
stopTime = UserDefaults.standard.object(forKey: stopTimeKey) as? Date
if let time = stopTime {
if time > Date() {
startTimer(time, includeNotification: false)
} else {
notifyTimerCompleted()
}
}
}
@IBAction func didTapStartButton(_ sender: Any) {
let time = datePicker.date
if time > Date() {
startTimer(time)
} else {
timerLabel.text = "timer date must be in future"
}
}
}
// MARK: Timer stuff
private extension ViewController {
func registerForLocalNotifications() {
if #available(iOS 10, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
guard granted, error == nil else {
// display error
print(error ?? "Unknown error")
return
}
}
} else {
let types: UIUserNotificationType = [.alert, .sound, .badge]
let settings = UIUserNotificationSettings(types: types, categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
}
func startTimer(_ stopTime: Date, includeNotification: Bool = true) {
// save `stopTime` in case app is terminated
UserDefaults.standard.set(stopTime, forKey: stopTimeKey)
self.stopTime = stopTime
// start Timer
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: true)
guard includeNotification else { return }
// start local notification (so we're notified if timer expires while app is not running)
if #available(iOS 10, *) {
let content = UNMutableNotificationContent()
content.title = "Timer expired"
content.body = "Whoo, hoo!"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: stopTime.timeIntervalSinceNow, repeats: false)
let notification = UNNotificationRequest(identifier: "timer", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(notification)
} else {
let notification = UILocalNotification()
notification.fireDate = stopTime
notification.alertBody = "Timer finished!"
UIApplication.shared.scheduleLocalNotification(notification)
}
}
func stopTimer() {
timer?.invalidate()
}
// I'm going to use `DateComponentsFormatter` to update the
// label. Update it any way you want, but the key is that
// we're just using the scheduled stop time and the current
// time, but we're not counting anything. If you don't want to
// use `DateComponentsFormatter`, I'd suggest considering
// `Calendar` method `dateComponents(_:from:to:)` to
// get the number of hours, minutes, seconds, etc. between two
// dates.
@objc func handleTimer(_ timer: Timer) {
let now = Date()
if stopTime! > now {
timerLabel.text = dateComponentsFormatter.string(from: now, to: stopTime!)
} else {
stopTimer()
notifyTimerCompleted()
}
}
func notifyTimerCompleted() {
timerLabel.text = "Timer done!"
}
}
顺便说一句,以上内容还说明了本地通知的使用(以防定时器在应用当前未运行时到期)。
这是在它自己的函数中,在同一个文件中: 你知道如何使用YouTube SDK在后台继续播放声音吗?还有,是不是可以只播放背景中的声音,不播放视频? 感谢任何帮助。
本文向大家介绍bootstrap快速制作后台界面,包括了bootstrap快速制作后台界面的使用技巧和注意事项,需要的朋友参考一下 最近看了bootstrap的一个小的视频,快速的做出一个后台界面;介绍了一些典型的用法; 里面涉及了: 下拉菜单、胶囊菜单、胶囊菜单垂直显示、栅格排列、导航栏、字体图标、 图片样式、输入组、折叠菜单panel、面包屑、表格样式、分页组件样式; 下面将跟着项目做出的小例
问题内容: 有什么方法可以知道我的应用程序的状态是后台模式还是前台模式。谢谢 问题答案: 将返回应用程序的当前状态,例如: UIApplicationStateActive UIApplicationStateInactive UIApplicationStateBackground 迅捷3+ 目标C
移动互联快速开发平台 采用Mongodb为底层数据库:数据设计随需而变; 采用Mongodb集群,支撑大数据量,大并发实时查询,便于扩展; 采用SpringMongodb简化开发,简单得令人发指; 采用SpringRest提供JSON的输出,支持各种转换; 提供程序整合、兼容中文、跨域JSONP的支持; 进行了大数据量的压力测试,参数的最优配置; 各种最佳实践。 HTML5 快速开发的前端架构,专
问题内容: 我希望iOS11版本能够解决无提示推送问题,该问题已在最新的Beta和GM版本的iOS中出现。 目前,我一直在努力理解为什么我没有收到任何静默的推送消息,这实际上应该唤醒我的应用程序以在后台执行一些所需的任务。 在iOS 10中,我仅使用后台获取功能,并在AppDelegate中实现了“唤醒代码”,如以下代码所示。 在iOS 11中,注册代码仍然可以正常工作,我的后端还向Apple D
快Star,没有笔试 没有八股,更多的是聊了聊实习经历 面试官感觉蛮aggressive的 说的是给30分钟做几道题,结果第一题反转链表就寄了(感觉快手很爱考这道题),太久没写,犯了个愚蠢的小错误,调了半天,最后只做出来这一道题 估计要挂了 最后问了问部门情况,感觉这个JD也蛮奇怪的,既有toC的大模型业务开发(这和其他后台开发有啥不一样的?),也有给算法爷的AI Infra。