美好的一天,
我正在开发一种运动应用程序,除非将其移动到背景时才能正常工作。计时器暂停时。我找到了一个我工作的背景计时器的例子,但现在我无法获得显示锻炼持续时间的UILabel。在控制台中它声明我正在从我理解的主线程中访问一个对象。我不知道该怎么做是让UILabel更新,因为定时器在后台线程中更新,更新标签位于主线程中。
这就是我所拥有的(打印语句帮助我遵循代码):
import UIKit
class ViewController: UIViewController {
var time = 0
var timer = Timer()
@IBOutlet weak var outputLabel: UILabel!
@IBOutlet weak var start: UIButton!
@IBOutlet weak var paused: UIButton!
@IBAction func startButton(_ sender: UIButton) {
startButtonPressed()
}
@IBAction func pausedButton(_ sender: UIButton) {
pausedButtonPressed()
}
@IBOutlet weak var timerLabel: UILabel!
func updateTimerLabel() {
let hours = Int(self.time) / 3600
let minutes = Int(self.time) / 60 % 60
let seconds = Int(self.time) % 60
timerLabel.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
func startButtonPressed() {
outputLabel.text = "Workout Started"
start.isHidden = true
paused.isHidden = false
_backgroundTimer(repeated: true)
print("Calling _backgroundTimer(_:)")
}
func pausedButtonPressed(){
outputLabel.text = "Workout Paused"
timer.invalidate()
pauseWorkout()
}
func pauseWorkout(){
paused.isHidden = true
start.isHidden = false
}
func _backgroundTimer(repeated: Bool) -> Void {
NSLog("_backgroundTimer invoked.");
//The thread I used is a background thread, dispatch_async will set up a background thread to execute the code in the block.
DispatchQueue.global(qos:.userInitiated).async{
NSLog("NSTimer will be scheduled...");
//Define a NSTimer
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self._backgroundTimerAction(_:)), userInfo: nil, repeats: true);
print("Starting timer")
//Get the current RunLoop
let runLoop:RunLoop = RunLoop.current;
//Add the timer to the RunLoop
runLoop.add(self.timer, forMode: RunLoopMode.defaultRunLoopMode);
//Invoke the run method of RunLoop manually
NSLog("NSTimer scheduled...");
runLoop.run();
}
}
@objc func _backgroundTimerAction(_ timer: Foundation.Timer) -> Void {
print("_backgroundTimerAction(_:)")
time += 1
NSLog("time count -> \(time)");
}
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad()")
print("Hiding buttons")
paused.isHidden = true
start.isHidden = false
print("Clearing Labels")
outputLabel.text = ""
timerLabel.text = ""
print("\(timer)")
timer.invalidate()
time = 0
}
}
以下是视图控制器的快照,我想更新持续时间。
非常感谢任何人提供的任何帮助。
此致
凯文
答案 0 :(得分:0)
不要尝试在后台运行计时器,而是记录锻炼开始的startDate
并计算时间间隔。这样,应用程序实际上不必在后台运行以跟踪锻炼时间。计时器仅用于更新用户界面。
暂停现在可以记录当前的锻炼间隔。当锻炼重新开始时,它会从Date()
中减去当前的锻炼间隔,以获得新的调整startDate
。
为进入后台和前台的应用添加通知,以便在锻炼处于活动状态时重新启动UI更新计时器:
import UIKit
enum WorkoutState {
case inactive
case active
case paused
}
class ViewController: UIViewController {
var workoutState = WorkoutState.inactive
var workoutInterval = 0.0
var startDate = Date()
var timer = Timer()
@IBOutlet weak var outputLabel: UILabel!
@IBOutlet weak var start: UIButton!
@IBOutlet weak var paused: UIButton!
@IBAction func startButton(_ sender: UIButton) {
startButtonPressed()
}
@IBAction func pausedButton(_ sender: UIButton) {
pausedButtonPressed()
}
@IBOutlet weak var timerLabel: UILabel!
func updateTimerLabel() {
let interval = -Int(startDate.timeIntervalSinceNow)
let hours = interval / 3600
let minutes = interval / 60 % 60
let seconds = interval % 60
timerLabel.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
func startButtonPressed() {
if workoutState == .inactive {
startDate = Date()
} else if workoutState == .paused {
startDate = Date().addingTimeInterval(-workoutInterval)
}
workoutState = .active
outputLabel.text = "Workout Started"
start.isHidden = true
paused.isHidden = false
updateTimerLabel()
_foregroundTimer(repeated: true)
print("Calling _foregroundTimer(_:)")
}
func pausedButtonPressed(){
// record workout duration
workoutInterval = floor(-startDate.timeIntervalSinceNow)
outputLabel.text = "Workout Paused"
workoutState = .paused
timer.invalidate()
pauseWorkout()
}
func pauseWorkout(){
paused.isHidden = true
start.isHidden = false
}
func _foregroundTimer(repeated: Bool) -> Void {
NSLog("_foregroundTimer invoked.");
//Define a Timer
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerAction(_:)), userInfo: nil, repeats: true);
print("Starting timer")
}
@objc func timerAction(_ timer: Timer) {
print("timerAction(_:)")
self.updateTimerLabel()
}
@objc func observerMethod(notification: NSNotification) {
if notification.name == .UIApplicationDidEnterBackground {
print("app entering background")
// stop UI update
timer.invalidate()
} else if notification.name == .UIApplicationDidBecomeActive {
print("app entering foreground")
if workoutState == .active {
updateTimerLabel()
_foregroundTimer(repeated: true)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(observerMethod), name: .UIApplicationDidEnterBackground, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(observerMethod), name: .UIApplicationDidBecomeActive, object: nil)
print("viewDidLoad()")
print("Hiding buttons")
paused.isHidden = true
start.isHidden = false
print("Clearing Labels")
outputLabel.text = ""
timerLabel.text = ""
print("\(timer)")
timer.invalidate()
}
}
原始答案
只需在主循环上调用updateTimerLabel()
:
DispatchQueue.main.async {
self.updateTimerLabel()
}
全功能:
@objc func _backgroundTimerAction(_ timer: Timer) {
print("_backgroundTimerAction(_:)")
time += 1
DispatchQueue.main.async {
self.updateTimerLabel()
}
NSLog("time count -> \(time)")
}
注意:
-> Void
添加到Swift函数定义中;这是默认值。;
,所以丢失它们。 self.time
已经是Int
,因此无需从中创建新的Int
。
取代:
let hours = Int(self.time) / 3600
使用:
let hours = self.time / 3600