如何在一个 viewController 中管理多个计时器? [英] How to manage multiple timers in one viewController?
问题描述
我正在开发计时器应用程序,但无法理解如何为每个单元格设置多个计时器.
我在 didSelectRowAt 处启动和暂停计时器:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {tableView.deselectRow(at: indexPath, 动画: true)let cell = tableView.cellForRow(at: indexPath) as!定时器表视图单元格让 item = timers.items[indexPath.row]item.toggle()打印(项目)startPauseTimer(for: cell, with: item)}
这是我的 startPauseTimer 代码:
var timer = Timer()func startPauseTimer(单元格:TimerTableViewCell,项目:定时器){如果 !item.isStarted {timer.invalidate()} 别的 {timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {timer initem.seconds -= 1cell.timerTime.text = self.formattedTime(time: TimeInterval(item.seconds))如果 item.seconds <1 {self.timer.invalidate()cell.timerTime.text = self.formattedTime(time: TimeInterval(item.seconds))item.isStarted = false}}}}
还有我的数据模型:
class Timers: NSObject, Codable {变量名 = ""var id = ""var 秒 = 0var 编辑秒 = 0var isStarted = false功能切换(){isStarted = !isStarted}
任何我的 Cell 代码:
class TimerTableViewCell: UITableViewCell {@IBOutlet var timerLabel:UILabel!@IBOutlet var timerTime: UILabel@IBOutlet var startPauseButton:UIButton!@IBOutlet var resetButton:UIButton!}
如何同时管理多个计时器?当我使用 didSelectRowAt 时,只有同一个 Timer() 实例在触发,所以多个计时器正在混合.我如何划分多个计时器并使它们工作?
这里是一个完整的例子,基于
I'm working on Timers app and cannot understand how I can make work multiple timers for each cell.
I start and pause timers at didSelectRowAt:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let cell = tableView.cellForRow(at: indexPath) as! TimerTableViewCell
let item = timers.items[indexPath.row]
item.toggle()
print(item)
startPauseTimer(for: cell, with: item)
}
And this is my code for startPauseTimer:
var timer = Timer()
func startPauseTimer(for cell: TimerTableViewCell, with item: Timers) {
if !item.isStarted {
timer.invalidate()
} else {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {timer in
item.seconds -= 1
cell.timerTime.text = self.formattedTime(time: TimeInterval(item.seconds))
if item.seconds < 1 {
self.timer.invalidate()
cell.timerTime.text = self.formattedTime(time: TimeInterval(item.seconds))
item.isStarted = false
}
}
}
}
And my data model:
class Timers: NSObject, Codable {
var name = ""
var id = ""
var seconds = 0
var editSeconds = 0
var isStarted = false
func toggle() {
isStarted = !isStarted
}
Any my Cell code:
class TimerTableViewCell: UITableViewCell {
@IBOutlet var timerLabel: UILabel!
@IBOutlet var timerTime: UILabel
@IBOutlet var startPauseButton: UIButton!
@IBOutlet var resetButton: UIButton!
}
How I can manage multiple timers at once? When I use didSelectRowAt only the same Timer() instance is firing, so multiple timers is mixing. How I can divide multiple timers and make them work?
Here is a complete example, based on iOS Timer Tutorial by Fabrizio Brancati at RayWenderlich.com
Everything is done via code (no @IBOutlet
or @IBAction
connections needed), so just create a new UITableViewController
and assign its custom class to ExampleTableViewController
:
ExampleTableViewController.swift
//
// ExampleTableViewController.swift
// MultipleTimers
//
// Created by Don Mag on 5/12/20.
//
import UIKit
class ExampleTableViewController: UITableViewController {
let cellID: String = "TaskCell"
var taskList: [Task] = []
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// -1 means use default 2-hours
let sampleData: [(String, Double)] = [
("First (2 hours)", -1),
("Second (2 hours)", -1),
("Third (10 seconds)", 10),
("Fourth (30 seconds)", 30),
("Fifth (1 hour 10 minutes)", 60 * 70),
("Sixth (2 hours)", -1),
("Seventh (45 minutes)", 60 * 45),
("Eighth (2 hours)", -1),
("Ninth (1 hour 10 minutes)", 60 * 70),
("Tenth (2 hours)", -1),
("Eleventh (45 minutes)", 60 * 45),
("Thirteenth (2 hours)", -1),
("Fourteenth (2 minutes)", 60 * 2),
("Fifthteenth (11 minutes)", 60 * 11),
("Sixteenth (2 hours)", -1),
]
sampleData.forEach { (s, t) in
let task = Task(name: s, targetTime: t)
self.taskList.append(task)
}
tableView.register(TaskCell.self, forCellReuseIdentifier: cellID)
createTimer()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return taskList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TaskCell
cell.task = taskList[indexPath.row]
return cell
}
}
// MARK: - Timer
extension ExampleTableViewController {
func createTimer() {
if timer == nil {
let timer = Timer(timeInterval: 1.0,
target: self,
selector: #selector(updateTimer),
userInfo: nil,
repeats: true)
RunLoop.current.add(timer, forMode: .common)
timer.tolerance = 0.1
self.timer = timer
}
}
func cancelTimer() {
timer?.invalidate()
timer = nil
}
@objc func updateTimer() {
guard let visibleRowsIndexPaths = tableView.indexPathsForVisibleRows else {
return
}
for indexPath in visibleRowsIndexPaths {
if let cell = tableView.cellForRow(at: indexPath) as? TaskCell {
cell.updateTime()
}
}
}
}
TaskCell.swift
//
// TaskCell.swift
// MultipleTimers
//
// Created by Don Mag on 5/12/20.
//
import UIKit
class TaskCell: UITableViewCell {
let taskNameLabel: UILabel = {
let v = UILabel()
v.textAlignment = .center
return v
}()
let timerLabel: UILabel = {
let v = UILabel()
v.textAlignment = .center
v.font = UIFont.monospacedDigitSystemFont(ofSize: 17.0, weight: .medium)
return v
}()
let actionButton: UIButton = {
let v = UIButton()
v.setTitle("Start", for: [])
v.setTitleColor(.lightGray, for: .highlighted)
v.setTitleColor(.darkGray, for: .disabled)
v.backgroundColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
return v
}()
let resetButton: UIButton = {
let v = UIButton()
v.setTitle("Reset", for: [])
v.setTitleColor(.lightGray, for: .highlighted)
v.setTitleColor(.darkGray, for: .disabled)
v.backgroundColor = .red
return v
}()
let buttonStack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 16
return v
}()
var task: Task? {
didSet {
taskNameLabel.text = task?.name
timerLabel.text = "0"
setState()
updateTime()
}
}
func setState() -> Void {
switch task?.state {
case .running:
actionButton.setTitle("Pause", for: [])
actionButton.isEnabled = true
case .paused:
if task?.elapsedTime == 0 {
actionButton.setTitle("Start", for: [])
actionButton.isEnabled = true
} else {
actionButton.setTitle("Resume", for: [])
actionButton.isEnabled = true
}
default: // .completed
actionButton.setTitle("", for: [])
actionButton.isEnabled = false
}
}
func updateTime() {
guard let task = task else {
return
}
var t: Double = 0
if task.state == .paused {
t = task.targetTime - task.elapsedTime
} else {
t = task.targetTime - (Date().timeIntervalSince(task.creationDate) + task.elapsedTime)
}
let tm = Int(max(t, 0))
let hours = tm / 3600
let minutes = tm / 60 % 60
let seconds = tm % 60
let s = String(format: "%02d:%02d:%02d", hours, minutes, seconds)
timerLabel.text = s
timerLabel.textColor = tm > 0 ? .black : .red
if tm == 0 {
task.state = .completed
setState()
}
}
@objc
func buttonTapped(_ sender: UIButton) -> Void {
guard let s = sender.currentTitle, let task = task else { return }
switch s {
case "Start", "Resume":
task.state = .running
task.creationDate = Date()
case "Pause":
task.state = .paused
task.elapsedTime += Date().timeIntervalSince(task.creationDate)
case "Reset":
task.state = .paused
task.elapsedTime = 0
default:
break
}
setState()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
[buttonStack, resetButton, actionButton, timerLabel, taskNameLabel].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
buttonStack.addArrangedSubview(actionButton)
buttonStack.addArrangedSubview(resetButton)
contentView.addSubview(buttonStack)
contentView.addSubview(taskNameLabel)
contentView.addSubview(timerLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
buttonStack.topAnchor.constraint(equalTo: g.topAnchor),
buttonStack.centerXAnchor.constraint(equalTo: g.centerXAnchor),
buttonStack.widthAnchor.constraint(equalToConstant: 280.0),
taskNameLabel.topAnchor.constraint(equalTo: buttonStack.bottomAnchor, constant: 8.0),
taskNameLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
taskNameLabel.widthAnchor.constraint(equalTo: buttonStack.widthAnchor),
timerLabel.topAnchor.constraint(equalTo: taskNameLabel.bottomAnchor, constant: 8.0),
timerLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
timerLabel.widthAnchor.constraint(equalTo: buttonStack.widthAnchor),
timerLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
actionButton.addTarget(self, action: #selector(self.buttonTapped(_:)), for: .touchUpInside)
resetButton.addTarget(self, action: #selector(self.buttonTapped(_:)), for: .touchUpInside)
}
}
Task.swift
//
// Task.swift
// MultipleTimers
//
// Created by Don Mag on 5/12/20.
//
import Foundation
enum TimerState {
case paused, running, completed
}
class Task {
let name: String
var creationDate = Date()
var elapsedTime: Double = 0
var state: TimerState = .paused
var targetTime: Double = 60 * 60 * 2 // default 2 hours
init(name: String, targetTime: Double) {
self.name = name
if targetTime != -1 {
self.targetTime = targetTime
}
}
}
And here's how it looks while running:
这篇关于如何在一个 viewController 中管理多个计时器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!