Ios 在UICollectionViewCell中添加带计时器的倒计时标签的最佳方法,即使在重复使用单元格后也可以正常工作

Ios 在UICollectionViewCell中添加带计时器的倒计时标签的最佳方法,即使在重复使用单元格后也可以正常工作,ios,swift,swift3,timer,uicollectionviewcell,Ios,Swift,Swift3,Timer,Uicollectionviewcell,我有一个在UICollectionView中呈现给用户的项目列表。这些项目具有倒计时标签,以显示项目可用的剩余时间。 我在UICollectionViewCell中使用了计时器来显示剩余时间,如: OperationQueue.main.addOperation { var remaingTimeInterval = self.calculateRemainigTime(remainingTime: remainingTime) if remaingTimeInt

我有一个在
UICollectionView
中呈现给用户的项目列表。这些项目具有倒计时标签,以显示项目可用的剩余时间。 我在
UICollectionViewCell
中使用了
计时器
来显示剩余时间,如:

OperationQueue.main.addOperation {
        var remaingTimeInterval = self.calculateRemainigTime(remainingTime: remainingTime)
        if remaingTimeInterval > 0 {
            self.timerLabel.isHidden = false
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] (_) in
                let hours = Int(remaingTimeInterval) / 3600
                let minutes = Int(remaingTimeInterval) / 60 % 60
                let seconds = Int(remaingTimeInterval) % 60
                self?.timerLabel?.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
                remaingTimeInterval -= 1
            })
        } else {
            self.timer?.invalidate()
            self.timerLabel.isHidden = true
        }
    }
这就是我根据给定日期计算剩余时间的方法:

//Calculating remaining time based on the item endDate and currentDAte
    func calculateRemainigTime(remainingTime: String) -> Int {
        let prizeRemainingTime = Helper.stringToDate(remainingTime)
        let prizeRemainingTimeInterval = Int(prizeRemainingTime.timeIntervalSince1970)
        let currentTimeInterval = Int(Date().timeIntervalSince1970)
        return prizeRemainingTimeInterval - currentTimeInterval
    }
在重新使用电池之前,一切都正常,此后倒计时数字不再正确

这是在UICollectionViewCell中显示倒计时的正确方法,还是有更好的解决方案


有人能帮我找到一种解决方法吗?

我实现了这种更简单的方法(不考虑性能/延迟)

在..cellForItemAt。。方法,我打电话

cell.localEndTime = yourEndTimeInterval
cell.startTimerProgress()
在“集合视图”单元中,我添加了一个启动进度的方法:

    var localEndTime: TimeInterval = 0 //set value from model
    var timer:Timer?
    func timerIsRunning(timer: Timer){
        let diff = localEndTime  - Date().timeIntervalSince1970
        if diff  > 0 {
            self.showProgress()
            return
        }
        self.stopProgress()


    }
    func showProgress(){
        let endDate = Date(timeIntervalSince1970: localEndTime)
        let nowDate = Date()
        let components = Calendar.current.dateComponents(Set([.day, .hour, .minute, .second]), from: nowDate, to: endDate)
        self?.timerLabel?.text = String(format:"%02i:%02i:%02i", components.hour!, components.minute!, components.second!)

    }

    func stopProgress(){
         self?.timerLabel?.text = String(format:"%02i:%02i:%02i", 0, 0, 0)
        self.timer?.invalidate()
        self.timer = nil
    }

    func startTimerProgress() {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerIsRunning(timer:)), userInfo: nil, repeats: true)
        self.timer?.fire()
    }

我实现了这种更简单的方法(不考虑性能/延迟)

在..cellForItemAt。。方法,我打电话

cell.localEndTime = yourEndTimeInterval
cell.startTimerProgress()
在“集合视图”单元中,我添加了一个启动进度的方法:

    var localEndTime: TimeInterval = 0 //set value from model
    var timer:Timer?
    func timerIsRunning(timer: Timer){
        let diff = localEndTime  - Date().timeIntervalSince1970
        if diff  > 0 {
            self.showProgress()
            return
        }
        self.stopProgress()


    }
    func showProgress(){
        let endDate = Date(timeIntervalSince1970: localEndTime)
        let nowDate = Date()
        let components = Calendar.current.dateComponents(Set([.day, .hour, .minute, .second]), from: nowDate, to: endDate)
        self?.timerLabel?.text = String(format:"%02i:%02i:%02i", components.hour!, components.minute!, components.second!)

    }

    func stopProgress(){
         self?.timerLabel?.text = String(format:"%02i:%02i:%02i", 0, 0, 0)
        self.timer?.invalidate()
        self.timer = nil
    }

    func startTimerProgress() {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerIsRunning(timer:)), userInfo: nil, repeats: true)
        self.timer?.fire()
    }
  • 将计时器逻辑移到数据模型
  • 使用
    定时器的基于块的API来代替目标/动作
  • cellForRow
    中,将计时器回调块传递给单元格
  • 当计时器触发时,回调块中的代码可以更新单元格中的UI
      • 将计时器逻辑移到数据模型
      • 使用
        定时器的基于块的API来代替目标/动作
      • cellForRow
        中,将计时器回调块传递给单元格
      • 当计时器触发时,回调块中的代码可以更新单元格中的UI

      根据公认的答案指南编码,希望您喜欢

      MyViewController

      protocol MyViewControllerDelegate : class {
          func updateTimer(_ timeString: String)
      }
      
      class MyViewController: UIViewController {
      
          weak var timerDetegate: MyViewControllerDelegate?
      
          var timer = Timer()
          var time = 0
      
          fun ViewDidLoad() {
              timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
                  self.time += 1
                  self.timerDetegate?.updateTimer("time \(self.time)")
              })
          }
      
          ...
      
          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
              let cell = tableViewdequeueReusableCell(withIdentifier: "MeCell")
              vc.timerDetegate = cell
              return cell
          }
      
          ...
      
      }
      
      MyTableViewCell

      class MyTableViewCell : UITableViewCell, MyViewControllerDelegate {
      
          ...
      
          func updateTimer(_ timeString: String) {
              lblTimeLeft.text = timeString
          }
      
      }
      

      根据公认的答案指南编码,希望您喜欢

      MyViewController

      protocol MyViewControllerDelegate : class {
          func updateTimer(_ timeString: String)
      }
      
      class MyViewController: UIViewController {
      
          weak var timerDetegate: MyViewControllerDelegate?
      
          var timer = Timer()
          var time = 0
      
          fun ViewDidLoad() {
              timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
                  self.time += 1
                  self.timerDetegate?.updateTimer("time \(self.time)")
              })
          }
      
          ...
      
          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
              let cell = tableViewdequeueReusableCell(withIdentifier: "MeCell")
              vc.timerDetegate = cell
              return cell
          }
      
          ...
      
      }
      
      MyTableViewCell

      class MyTableViewCell : UITableViewCell, MyViewControllerDelegate {
      
          ...
      
          func updateTimer(_ timeString: String) {
              lblTimeLeft.text = timeString
          }
      
      }
      

      更好的解决方案是在数据模型中而不是在视图中运行计时器。你是什么意思?是否在数据模型中添加属性以节省剩余时间?那么如何每秒更新它呢?例如,您可以在模型中运行计时器,并将
      cellForRow
      中的操作或回调传递给cell。我认为这不起作用,因为
      cellForRow
      仅在要重用cell时才被调用,但是我必须每秒钟显示一次倒计时。为了让它更清楚:您使用基于块的API
      Timer
      ,并将
      cellForRow
      中的块传递给单元格。只要计时器启动时单元格可见,它就会调用更新UI的块。更好的解决方案是在数据模型中而不是在视图中运行计时器。你是什么意思?是否在数据模型中添加属性以节省剩余时间?那么如何每秒更新它呢?例如,您可以在模型中运行计时器,并将
      cellForRow
      中的操作或回调传递给cell。我认为这不起作用,因为
      cellForRow
      仅在要重用cell时才被调用,但是我必须每秒钟显示一次倒计时。为了让它更清楚:您使用基于块的API
      Timer
      ,并将
      cellForRow
      中的块传递给单元格。只要计时器触发时单元格可见,它就会调用更新UI的块。