【发布时间】:2015-10-17 03:30:07
【问题描述】:
更新:此代码已使用以下答案建议的最新修复程序进行了更新。感谢所有提供帮助的人。
我正在创建一个应用程序,其中有几个计时器显示在 UITableView 中,如这些图像 Timers List 和 Menu 所示。
我遵循 MVC 范例,并将模型、控制器和视图相互分离。所以我有
- 计时器类,计时器所在的位置。
- 一个 UITableViewController
- 一个 UITableViewCell
- 一个 UIViewController,我在其中配置计时器。
基本上一切正常,除了我无法在每个单元格的标签中“显示”计时器。请注意,我已经进行了 6 个月的编码,这将是我第一个拥有 UITableView 并学习 MVC 基础知识的应用程序。
所以应用程序的工作原理是用户添加一个新计时器,然后通过点击“开始”按钮,计时器应该开始倒计时。这些是 NSTimers。单击开始后,计时器将被触发并运行,但它们不会在标签上显示给用户。这就是我的问题所在。
如果有人有任何建议或可以帮助我解决这个问题,我将非常感激。
这是我的代码。
定时器类:
@objc protocol Reloadable: class {
@objc optional func reloadTime()
}
class Timer {
// MARK: Properties
var time: Int
var displayTime: Int
var photo: UIImage
weak var dataSource: Reloadable?
// MARK: - Methods
init(time: Int, displayTime: Int, photo: UIImage){
self.time = time
self.displayTime = displayTime
self.photo = photo
}
/// The Timer properties and Methods start from here ------
// MARK: - Timer Properties
var counterRun = NSTimer()
var colorRun = NSTimer()
var startTime = NSTimeInterval()
var currentTime = NSTimeInterval()
// MARK: - Timer Mothods
func startTimeRunner(){
counterRun = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector:"timeRunner:", userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
@objc func timeRunner(timer: NSTimer){
currentTime = NSDate.timeIntervalSinceReferenceDate()
let elapsedTime: NSTimeInterval = currentTime - startTime
///calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 1)
let minutesInt = Int(minutes)
displayTime = time - minutesInt
"reloadTime()" in the TimerTableVIewController.
if let myDelegate = self.dataSource {
myDelegate.reloadTime!()
}
}
}
TableViewController
class TimerTableViewController: UITableViewController, ButtonCellDelegate, Reloadable{
// MARK: Properties
var timers = [Timer]()
override func viewDidLoad() {
super.viewDidLoad()
/// Loads one timer when viewDidLoad
let time = Timer(time: 30, displayTime: 30, photo: UIImage(named: "Swan")!)
timers.append(time)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
self.navigationItem.leftBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timers.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TimerCell", forIndexPath: indexPath) as! TimerTableViewCell
let time = timers[indexPath.row]
cell.timeLabel.text = "\(time.displayTime)"
cell.photo.image = time.photo
/// Makes TimerTableViewController (self) as the delegate for TimerTableViewCell.
if cell.buttonDelegate == nil {
cell.buttonDelegate = self
}
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
timers.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
/// Unwind segue, the source is the MenuViewController.
@IBAction func unwindToTimerList(sender: UIStoryboardSegue){
if let sourceViewController = sender.sourceViewController as? MenuViewController, time = sourceViewController.timer {
let newIndexPath = NSIndexPath(forRow: timers.count, inSection: 0)
timers.append(time)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
/// With the help of the delegate from TimerTableViewCell, when the "start" button is pressed, it will
func cellTapped(cell: TimerTableViewCell) {
let cellRow = tableView.indexPathForCell(cell)!.row
let timer = timers[cellRow]
timer.dataSource = self
timer.startTimeRunner()
}
func reloadTime(){
if self.tableView.editing == false {
self.tableView.reloadData()
}
}
}
TableViewCell
protocol ButtonCellDelegate {
func cellTapped(cell: TimerTableViewCell)
}
class TimerTableViewCell: UITableViewCell, UITextFieldDelegate{
// MARK: Properties
@IBOutlet weak var startButtonOutlet: UIButton!
@IBOutlet weak var refreshButtonOutlet: UIButton!
@IBOutlet weak var timeLabel: UILabel!
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var photo: UIImageView!
var buttonDelegate: ButtonCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
/// UITextFieldDelegate to hide the keyboard.
textField.delegate = self
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
@IBAction func startButton(sender: UIButton) {
if let delegate = buttonDelegate {
delegate.cellTapped(self)
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
/// Hide the keyboard
textField.resignFirstResponder()
return true
}
}
还有 MenuViewController
class MenuViewController: UIViewController {
// MARK: Properties
@IBOutlet weak var swanPhoto: UIImageView!
@IBOutlet weak var duckPhoto: UIImageView!
@IBOutlet weak var minsPhoto: UIImageView!
@IBOutlet weak var okButtonOutlet: UIButton!
var timer: Timer?
var photo: UIImage? = UIImage(named: "Swan")
var time: Int? = 30
var displayTime: Int? = 30
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Actions
@IBAction func swantButton(sender: UIButton) {
photo = UIImage(named: "Swan")
}
@IBAction func duckButton(sender: UIButton) {
photo = UIImage(named: "Duck")
}
@IBAction func okButton(sender: UIButton) {
}
@IBAction func cancelButton(sender: UIButton) {
self.dismissViewControllerAnimated(true, completion: nil)
}
@IBAction func min60(sender: UIButton) {
time = 60
displayTime = 60
}
@IBAction func min30(sender: UIButton) {
time = 30
displayTime = 30
}
@IBAction func min15(sender: UIButton) {
time = 15
displayTime = 15
}
// MARK: Navegation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if okButtonOutlet === sender {
let photo = self.photo
let time = self.time
let displayTime = self.displayTime
timer = Timer(time: time!, displayTime: displayTime!, photo: photo!)
}
}
}
【问题讨论】:
-
您可以看到
cell.photo.image = time.photo,但看不到cell.timeLabel.text = "\(time.time)"? -
是的,照片和时间标签都可以看到,但那是您创建一个包含这些值的单元格的时候。但是当计时器运行时,标签不会更新。
-
这可能是因为您在计时器运行时没有重新加载单元格,因此单元格没有最新数据。尝试在单元格中创建一个视图并在视图中附加计时器标签。
-
等等什么?大声笑我,我是这些事情的新生儿哈哈。
-
即使您正在运行计时器,单元格也不会更新,因为没有调用
tableView.reloadData(),因此标签不知道您提供的新信息。我建议尝试将其附加到视图而不是单元格本身并让它以这种方式运行。
标签: ios iphone swift uitableview timer