【问题标题】:The detail text label on tableview cell does not refresh with countdown timertableview 单元格上的详细文本标签不使用倒数计时器刷新
【发布时间】:2019-11-21 09:38:05
【问题描述】:

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

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

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

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

总而言之,我想创建一个任务的倒数计时器功能,以显示它每秒倒数。我希望它在 viewdidload 上加载。

我的计时器正确吗?或在错误的地方。我认为如果它是 inn cellforRow 函数,让计时器刷新整个表格是有意义的。

我在 cellforrow 函数中尝试了一个计时器,但没有任何乐趣。然后,我为计时器创建了一个单独的函数,以在其中使用 reloadData() 刷新 tableview。该函数在 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

【问题讨论】:

  • 嗨 Rob 感谢您的回复 - 我正在使用 deadlineTimeDueString 作为全局变量。它在函数 updateTime() 中处理。对于所有设置了时间的单元格,我得到的值似乎都是正确的。我只希望字幕每秒刷新一次,从而直观地显示倒计时。 - 当您说添加日志时,我不确定我是否理解?喜欢向控制台添加打印语句?
  • 我明白了。感谢那。我对 Swift 还是很陌生,但我很欣赏 Rob 的建议。您认为使用一个计时器让表格重新加载是正确的做法吗?
  • 我同意秒对于 UX 来说可能有点太多了,我肯定会在更新时考虑这一点。我要做一些测试,然后从那里检查。将及时向大家发布。再次感谢您的帮助
  • 嗨 Rob - 我已经设法创造了一些回报,并对我从这些函数中得到的回报感到满意。我已经编辑了我的代码,并且还在其中插入了更新功能。 -- 您在之前的 cmets 中提到您可以更新单元格的标签。我该怎么办?亲切的问候 MFK

标签: swift uitableview timer countdown


【解决方案1】:

我可能会建议让单元格更新它们自己的标签,但是为了让它们同时更新,让控制器在单元格应该更新时发布通知。

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

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
        }
    }
}

注意,我使用的是DateComponentsFormatter2 中的maximumUnitCount,所以我看不到烦人的细节水平。因此,我将只看到“7 天 12 小时”,而不是“7 天 12 小时 42 分钟 8 秒”,但它会显示秒数可能相关的位置(例如,距离不到 1 小时):

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

【讨论】:

  • 非常感谢您抽出宝贵时间提供帮助!欣赏它,罗伯。你是明星!
猜你喜欢
  • 2012-03-02
  • 1970-01-01
  • 1970-01-01
  • 2020-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多