如何保存倒数计时器的进度?

问题描述:

因此,我正在swift中构建一个应用程序,为不同的任务设置一个长计时器。我希望能够保存定时器的进度,以便用户可以离开视图控制器并稍后再回来。如何保存倒数计时器的进度?

用户从tableView中选择一个任务,每个任务都有一定的时间附加到它,并被segued到一个实际的计时器。我希望用户能够离开计时器并留下剩余时间以在我的数据模型中进行更新。

我到目前为止似乎没有工作,我不知道该怎么做。以下代码供用户离开计时器并返回表格。 TaskData是我的数据模型。

@IBAction func cancel(_ sender: Any) { 
    let item = TaskData() 
    item.time = String(seconds) 
    dismiss(animated: true, completion: nil) 
    delegate?.viewController(self, didFinishEditing: item) 
} 

在我的表视图控制器我用下面

func viewController(_ controller: ViewController, didFinishEditing item: TaskData) { 
    if let index = tasks.index(of: item) { 
     let indexPath = IndexPath(row: index, section: 0) 
     if let cell = tableView.cellForRow(at: indexPath) { 
      configureText(for: cell, with: item) 
     } 
    } 
    dismiss(animated: true, completion: nil) 
    saveTasklistItems() 
} 

此功能时,我去表视图从定时器(视图控制器),然后返回到计时器,留在计时器的时间恢复到原来的状态。

这里是两个视图控制器

import UIKit 

class TaskListViewController: UITableViewController, 
AddNewTaskViewControllerDelegate, TimerViewControllerDelegate { 

var tasks: [TaskData] 

required init?(coder aDecoder: NSCoder) { 

    tasks = [TaskData]() 
    super.init(coder: aDecoder) 
    loadChecklistItems() 
} 


//DELEGATE PROTOCOLS 
func addNewTaskViewControllerDidCancel(_ controller: AddNewTaskViewController) { 
    dismiss(animated: true, completion: nil) 
} 
func addNewTaskViewController(_ controller: AddNewTaskViewController, didFinishAdding item: TaskData) { 
    let newRowIndex = tasks.count 
    tasks.append(item) 
    print(item.time) 

    let indexPath = IndexPath(row: newRowIndex, section: 0) 
    let indexPaths = [indexPath] 
    tableView.insertRows(at: indexPaths, with: .automatic) 

    dismiss(animated: true, completion: nil) 
    saveTasklistItems() 
} 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 
    if segue.identifier == "AddTask" { 
     let navigationController = segue.destination as! UINavigationController 
     let controller = navigationController.topViewController as! AddNewTaskViewController 
     controller.delegate = self 
    } else if segue.identifier == "ShowTimer" { 
     let navigationController = segue.destination as! UINavigationController 
     let controller = navigationController.topViewController as! ViewController 
     if let indexPath = tableView.indexPath(for: sender as! UITableViewCell) { 
      controller.timerTask = tasks[indexPath.row] 
      controller.timerTime = tasks[indexPath.row] 
     } 
    } 
} 

func viewController(_ controller: ViewController, didFinishEditing item: TaskData) { 
    if let index = tasks.index(of: item) { 
     let indexPath = IndexPath(row: index, section: 0) 
     if let cell = tableView.cellForRow(at: indexPath) { 
      configureText(for: cell, with: item) 
     } 
    } 
    dismiss(animated: true, completion: nil) 
    saveTasklistItems() 
} 
/////////////////////////////// 

func configureText(for cell: UITableViewCell, with item: TaskData) { 
    let label = cell.viewWithTag(2) as! UILabel 
    label.text = item.time 
} 

override func viewDidLoad() { 
    super.viewDidLoad() 

    // Do any additional setup after loading the view. 
} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 

    tasks.remove(at: indexPath.row) 
    let indexPaths = [indexPath] 
    tableView.deleteRows(at: indexPaths, with: .automatic) 
    saveTasklistItems() 
} 

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return tasks.count 
} 

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "TaskListItem", for: indexPath) 

    let taskItem = tasks[indexPath.row] 
    let label = cell.viewWithTag(1) as! UILabel 
    let label2 = cell.viewWithTag(2) as! UILabel 
    label.text = taskItem.task 
    label2.text = taskItem.time 
    return cell 
} 

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 

} 

//SAVE FUNCTIONALITY 
func documentsDirectory() -> URL { 
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) 
    return paths[0] 
} 
func dataFilePath() -> URL { 
    return documentsDirectory().appendingPathComponent("Tasklists.plist") 
} 
func saveTasklistItems() { 
    let data = NSMutableData() 
    let archiver = NSKeyedArchiver(forWritingWith: data) 
    archiver.encode(tasks, forKey: "TasklistItems") 
    archiver.finishEncoding() 
    data.write(to: dataFilePath(), atomically: true) 
} 

func loadChecklistItems() { 
    let path = dataFilePath() 
    if let data = try? Data(contentsOf: path) { 
     let unarchiver = NSKeyedUnarchiver(forReadingWith: data) 
     tasks = unarchiver.decodeObject(forKey: "TasklistItems") as! [TaskData] 
     unarchiver.finishDecoding() 
    } 
} 
} 

上面是的tableView的代码,下面我会后所述定时器

import UIKit 

protocol TimerViewControllerDelegate: class { 
func viewController(_ controller: ViewController, didFinishEditing item: TaskData) 
} 

class ViewController: UIViewController, UITextFieldDelegate { 

@IBOutlet weak var timerLabel: UILabel! 
@IBOutlet weak var pauseButton: UIButton! 
@IBOutlet weak var startButton: UIButton! 
@IBOutlet weak var timerTaskName: UILabel! 
@IBOutlet weak var timerTimeSetting: UILabel! 
@IBOutlet weak var progressView: UIProgressView! 

weak var delegate: TimerViewControllerDelegate? 

var timerTask: TaskData? 
var timerTime: TaskData? 
var seconds: Int = 0 
var timer = Timer() 
var isTimerRunning = false 
var resumeTapped = false 
var progressViewSpeed: Double = 0.0 

//THIS BUTTON CREATS AN UPDATED TASK DATA TIME MEMBER 
@IBAction func cancel(_ sender: Any) { 
    let item = TaskData() 
    item.time = String(seconds) 
    print(item.time) 
    dismiss(animated: true, completion: nil) 
    delegate?.viewController(self, didFinishEditing: item) 
} 
///////////////////////////// 


@IBAction func startButtonTapped(_ sender: Any) { 

    if isTimerRunning == false { 
     runTimer() 
     self.startButton.isEnabled = false 
    } 
    if seconds >= (500 * 60 * 60) { 
     seconds = (500 * 60 * 60) 
    } 
} 

func runTimer() { 
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true) 
    isTimerRunning = true 
    pauseButton.isEnabled = true 
} 

@IBAction func pauseButtonTapped(_ sender: UIButton) { 
    if self.resumeTapped == false { 
     timer.invalidate() 
     self.resumeTapped = true 
     self.pauseButton.setTitle("Resume",for: .normal) 
    } else { 
     runTimer() 
     self.resumeTapped = false 
     self.pauseButton.setTitle("Pause",for: .normal) 
    } 
} 

/* 
@IBAction func resetButtonTapped(_ sender: Any) { 
    timer.invalidate() 
    seconds = 60 
    self.timerLabel.text = timeString(time: TimeInterval(seconds)) 
    if self.resumeTapped == true { 
     self.resumeTapped = false 
     self.pauseButton.setTitle("Pause",for: .normal) 
    } 
    isTimerRunning = false 
    pauseButton.isEnabled = false 
    startButton.isEnabled = true 
} 
*/ 

func updateTimer() { 
    if seconds < 1 { 
     timer.invalidate() 
     //Send alert to indicate "time's up!" 
    } else { 
     seconds -= 1 
     timerLabel.text = timeString(time: TimeInterval(seconds)) 
    } 
    progressViewSpeed = 1/Double(seconds) 
    progressView.progress += Float(progressViewSpeed) 
} 

func timeString(time:TimeInterval) -> String { 
    let hours = Int(time)/3600 
    let minutes = Int(time)/60 % 60 
    let seconds = Int(time) % 60 
    return String(format:"%02i:%02i:%02i", hours, minutes, seconds) 
} 

override func viewDidLoad() { 
    super.viewDidLoad() 
    pauseButton.isEnabled = false 

    if let task = timerTask { 
     timerTaskName.text = task.task 
    } 

    if let timerTimeLeft = timerTime { 
     timerTimeSetting.text = timerTimeLeft.time 
    } 

    //I SOLVED A SERIOUS ERROR HERE 
    let timeLeft: Int? = Int(timerTimeSetting.text!) 
    seconds = Int(timeLeft!) * 60 * 60 
    self.timerLabel.text = timeString(time: TimeInterval(seconds)) 
    /////////////////////////////// 

    self.progressView.transform = CGAffineTransform.identity.rotated(by: CGFloat.pi/2).scaledBy(x: 1, y: 150) 


} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 
} 

保存TaskData的阵列,并且其相应的开始时间和/或结束时间并将该阵列保存到UserDefaults

+0

我该如何实施? – Tom

当您启动计时器时,将当前日期作为时间间隔保存到实例变量中:

定义实例变量:

var startTime: TimeInterval = 0.0 
var elapseTime: TimeInterval = 0.0 
let totalTime = 100.0 //Replace with your desired total time 

weak var timer: Timer? 

那么你可能会使用这样的代码:

@IBAction func startButtonTapped(_ sender: Any) { 
    //save the current date as a time interval 
    startTime = Date().timeIntervalSinceReferenceDate 
    timer = Timer.scheduledTimer(timeInterval: 1, 
    target: self, 
    selector: #selector(ViewController.updateTimer), 
    userInfo: nil, 
    repeats: true) 
} 

@IBAction func pauseTimer(_ sender: Any) { 
    timer.invalidate() 
    //Calculate the amount of time that's passed since starting the timer 
    elapsedTime = Date().timeIntervalSinceReferenceDate - startTime 

    //Calculate the amount of time remaining 
    timeRemaining = totalTime - elapsedTime 
    //Save timeRemaining to UserDefaults or do whatever you desire with it. 
} 

这是一个非常粗略的轮廓。你需要弄清细节,但这应该让你开始。

+0

我已经实现了这一点,这很有帮助,但我现在关心的是剩余时间不会在用户退回到表视图时更改,以便用户可以从停止的位置开始。这是我想要做的与我的取消按钮功能。为了给出一些上下文,您可以选择一个任务的名称和您想要处理的小时数,所以当用户退出应用程序或关闭应用程序时,剩下的时间应该保持不变。 – Tom

+0

好的......上面的代码概述了如何计算剩余时间。将该值保存到userDefaults,并在应用程序启动时读取它。你不了解什么部分? –

+0

嗯,我需要学习如何将内容保存到userDefaults,目前为止我只使用NSkeyedArchiver – Tom