TableView单元格上的详细信息文本标签不使用倒数计时器刷新

时间:2019-07-11 15:52:30

标签: swift uitableview timer countdown

我有一个带有基本cellforrow功能的表格视图。我还为我的待办事项清单应用程序构造了一个计时器功能。

我正在尝试使用标准字幕单元格创建一个在表视图的每个单元格中都具有运行倒计时计时器的应用程序。计时器名称显示在文本字段中,正在运行的计时器显示在字幕中。

这里的想法是,当用户为所选任务设置特定的截止日期时,计算任务的剩余时间。

我成功地实现了此功能,但是我遇到的问题是,当我将单元格(字幕)的detailtextlabel设置为剩余的剩余时间(显示剩余天数/小时/分钟/秒)时,它不会刷新。

总结一下,我想创建一个任务的倒数计时器功能,以每秒显示一次倒数。我希望它加载到viewdidload上。

我的计时器正确吗?或在错误的位置。我认为,如果这是inn cellforRow函数,那么让计时器刷新整个表将很有意义。

我在cellforrow函数中尝试了一个计时器,没有任何乐趣。然后,我为计时器创建了一个单独的函数,以使用内部的reloadData()刷新表视图。在viewDidLoad中调用此函数。倒计时并一切正常,但是除非单击单元格或添加新的待办任务,否则标签不会更新。

override func viewDidLoad() {
    super.viewDidLoad()

    func startTimer() {
        self.countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(reloadTableForTimer), userInfo: nil, repeats: true)
    }
}



@objc func reloadTableForTimer() {
    DispatchQueue.main.async { self.tableView.reloadData() }
}






override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    if item.done == true {
                cell.detailTextLabel?.text = "Complete!"
            }else {

                var deadlineDatesAll = item.deadlineDate

                if deadlineDatesAll == "" {
                    cell.detailTextLabel?.text = "NO DEADLINE SET"
                } else {
                    // insert code for data disection and then to timer to display each countdown:
                    retrieveDateComponants(deadlineDateChosen: deadlineDatesAll.description)
                    var dataComponants:Array = retrieveDateComponants(deadlineDateChosen: deadlineDatesAll.description)

                    var returnedDueDateString = updateTime(yearExtract: dataComponants[0], monthExtract: dataComponants[1], dayExtract: dataComponants[2], hourExtract: dataComponants[3], minExtract: dataComponants[4], secsExtract: dataComponants[5])
                    cell.detailTextLabel?.text = "Due In: \(returnedDueDateString)"
                }

            }
        cell.accessoryType = item.done == true ? .checkmark : .none
    }else {
        cell.textLabel?.text = "No Item Found"
    }
    return cell
}


//UPDATE countdowns function:
@objc func updateTime(yearExtract:String, monthExtract:String, dayExtract:String,hourExtract:String,minExtract:String, secsExtract:String) -> (String) {

        var deadlineTimeDueString : String = ""

        let futureDate: Date = {
            var future = DateComponents(
                year: Int(yearExtract),
                month: Int(monthExtract),
                day: Int(dayExtract),
                hour: Int(hourExtract),
                minute: Int(minExtract),
                second: Int(secsExtract)            )
            return Calendar.current.date(from: future)!
        }()

        var countdownComp: DateComponents {
            return Calendar.current.dateComponents([.day, .hour, .minute, .second], from: Date(), to: futureDate)
        }

        let countdown = countdownComp //only compute once per call
        let days = countdown.day!
        let hours = countdown.hour!
        let minutes = countdown.minute!
        let seconds = countdown.second!

        print((String(format: "OUTPUT TEST: %02d:%02d:%02d:%02d", days, hours, minutes, seconds)))

        if seconds < 0 {
            deadlineTimeDueString = String("OVERDUE")
        } else if seconds > 0 {
            deadlineTimeDueString = String(format: "%02d Days, %02d Hours, %02d Mins & %02d Secs", days, hours, minutes, seconds)
        }
        return deadlineTimeDueString
    }


一切正常,除了倒计时时间不会刷新标签,除非我手动单击另一个单元格或相同的单元格。剩余时间有效,每秒更新一次。

任何帮助将不胜感激 谢谢MFK

1 个答案:

答案 0 :(得分:0)

我可能建议让单元更新它们自己的标签,但是要使它们同时更新所有标签,并在应该更新单元时让控制器发布通知。

因此,您可以为计时器创建通知:

extension Notification.Name {
    static let timerFired = Notification.Name(rawValue: Bundle.main.bundleIdentifier! + ".timer")
}

然后,视图控制器将发布此通知:

class ViewController: UITableViewController {
    var todos = ...

    private weak var timer: Timer?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            NotificationCenter.default.post(name: .timerFired, object: nil)
        }
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        timer?.invalidate()
    }
}

然后UITableViewDataSource可以将“要做的事情”传递给单元格:

// MARK: - UITableViewDataSource

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoCell", for: indexPath) as! ToDoCell
        cell.configure(for: todos[indexPath.row])
        return cell
    }
}

然后该单元格可以将所有内容捆绑在一起,观察通知并适当更新文本:

class ToDoCell: UITableViewCell {
    private var todo: ToDo?
    private var token: NSObjectProtocol?

    private static let formatter: DateComponentsFormatter = {
        let formatter = DateComponentsFormatter()
        formatter.unitsStyle = .full
        formatter.allowedUnits = [.day, .hour, .minute, .second]
        formatter.maximumUnitCount = 2
        return formatter
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        addObservers()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        addObservers()
    }

    deinit {
        removeObservers()
    }

    func configure(for todo: ToDo) {
        self.todo = todo
        textLabel?.text = todo.name
        if todo.done {
            removeObservers()
            detailTextLabel?.textColor = .black
            detailTextLabel?.text = "Done!"
            accessoryType = .checkmark
        } else {
            addObservers()
            updateTime()
            accessoryType = .none
        }
    }
}

private extension ToDoCell {
    func addObservers() {
        guard token == nil else { return }

        token = NotificationCenter.default.addObserver(forName: .timerFired, object: nil, queue: .main) { [weak self] _ in
            self?.updateTime()
        }
    }

    func removeObservers() {
        if let token = token {
            NotificationCenter.default.removeObserver(token)
        }
    }

    func updateTime() {
        guard let date = todo?.deadline else {
            detailTextLabel?.text = "No deadline specified"
            detailTextLabel?.textColor = .black
            return
        }

        let now = Date()
        if date < now {
            detailTextLabel?.text = "Past due " + (ToDoCell.formatter.string(from: date, to: now) ?? "")
            detailTextLabel?.textColor = .red
        } else {
            detailTextLabel?.text = ToDoCell.formatter.string(from: now, to: date)
            detailTextLabel?.textColor = .black
        }
    }
}

请注意,我在DateComponentsFormatter中使用maximumUnitCount2,所以我看不到令人讨厌的详细程度。因此,我只会看到“ 7天12小时”,而不是“ 7天12小时42分8秒”,但是它将显示与秒数相关的位置(例如,距离不到1小时):

enter image description here

但是,细节比一般的设计原理重要得多,一般的设计原理是将大量特定于单元格的代码拉入UITableViewCell子类中,而不是将其放入视图控制器中。