使用计时器每秒更改 collectionViewCell.label.text

Having collectionViewCell.label.text Change Every Second with Timer

我目前正在学习编写 swift 应用程序代码,并且正在尝试自己做一些项目。我目前的个人挑战是做一个倒计时应用程序。这个概念很简单:用户从 UIDatePicker 输入一个日期,应用程序显示剩余时间,直到他列出各种事件(使用用户默认值将事件保存在内存中)。所有上述事件都显示在集合视图中(请参阅下面的屏幕截图)。

我 运行 对我的实际技能来说太复杂了,我想你们可能会有答案,或者至少有一些建议!这是我的问题:我希望从今天到事件之间的剩余时间每秒减少,并通过 collectionViewCell 内的标签显示。我找到了一个非常简单的方法这样做,有一个计时器,但是用 collectionViewCell 实现上述解决方案让我很头疼。

以下是我想分享的代码摘录,希望足够了:

@objc func UpdateTimeLabel(index: Int) -> String {
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
    dateFormatter.timeZone = userCalendar.timeZone
    
    let date = dateFormatter.date(from: UserDefaults.standard.array(forKey: "dates")![index] as! String)!
    
    let timeLeft = userCalendar.dateComponents([.day, .hour, .minute, .second], from: currentDate, to: date)

    return "\(timeLeft.day!) days \(timeLeft.hour!) hours \(timeLeft.minute!) minutes \(timeLeft.second!) seconds"
    
}

这是我想要每秒触发的函数。它目前的制作方式是它的 属性,称为索引,是 collectionViewCell 的 indexPath.row。它引用存储为 UserDefaults 的事件日期数组。 collectionView 中的单元格与数组中的事件一样多。

参见下面我在 collectionView forItemAt 函数中实现它的方式:

   func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
            
        // Configure the state of the cell based on the propertires of the card if reprensents
        let cardCell = cell as? EventCollectionViewCell
        
        // UI Configuration
        cardCell?.eventTitleLabel.text = ((UserDefaults.standard.array(forKey: "events")![indexPath.row]) as! String)
        
        cardCell?.eventDateLabel.text = ("Event on: " + "\((UserDefaults.standard.array(forKey: "dates")![indexPath.row]) as! String)")

        cardCell?.countdownLabel.text = UpdateTimeLabel(index: indexPath.row)
    
        cardCell?.eventView.layer.cornerRadius = 10
          
    }

实际结果是每个单元格都显示从今天到事件之间的剩余时间。这已经超出了我自己的想象!!!

Results of actual code

我认为你们中的一个人可以帮助我回答这个问题:定时器应该放在哪里,看起来像下面的代码,以便 cardCell.countdownLabel.text每秒更新一次?

timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(UpdateTimeLabel), userInfo: nil, repeats: true)

我尝试了所有我能想到的方法,但现在遇到了障碍。我的项目中还有很多事情要解决,所以我还没有完全停止。如果您需要更多代码行 and/or 精度,请告诉我,我会为您提供您认为有用的任何内容。

非常感谢您抽出时间审阅我的问题,

路易斯·G

将此方法 UpdateTimeLabel 方法添加到 EventCollectionViewCell 中并像这样修改方法

@objc func UpdateTimeLabel() {
    let userCalendar = Calendar(identifier: .indian)
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
    dateFormatter.timeZone = userCalendar.timeZone
    
    let date = dateFormatter.date(from: UserDefaults.standard.array(forKey: "dates")![index] as! String)!
    
    let timeLeft = userCalendar.dateComponents([.day, .hour, .minute, .second], from: Date(), to: date)

    countdownLabel.text = "\(timeLeft.day!) days \(timeLeft.hour!) hours \(timeLeft.minute!) minutes \(timeLeft.second!) seconds"
    
}

完成此操作后,在 EventCollectionViewCell 中添加一个 属性 index 并在 EventCollectionViewCell 中的索引触发计时器 didSet 上添加一个 属性 index 例如:

var index: Int {
    didSet {
        let timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(UpdateTimeLabel), userInfo: nil, repeats: true)
        timer.fire()
    }
}

替换

cardCell?.countdownLabel.text = UpdateTimeLabel(index: indexPath.row)

cardCell?.index = indexPath.row

我找到了为什么@Satyen 建议的解决方案不起作用!好的,我知道,距离他上次回答已经差不多一个月了,但我不得不说,当我看到它没有修复我的应用程序并决定从事其他项目时,我感到非常沮丧,希望其他人能加入并提供另一个同时解决。无论如何,我今天早上决定通过几次会议找到问题,我很自豪地报告我做到了!

好的,这是出了什么问题: 我在今天使用了一个名为“当前日期”的变量。这个变量是在 UpdateTimeLabel 函数之外声明的,并在 bath 右侧的 dateComponents 中进行了转换。结果:当前日期并非每秒刷新一次,因此每次将事件日期与 currentDate 进行比较时,结果都保持不变。

这是工作代码:

@objc func UpdateTimeLabel() {

    let userCalendar = Calendar.current
    let todayDate = Date()
    let components = userCalendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: todayDate)
    let currentDate = userCalendar.date(from: components)!
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
    dateFormatter.timeZone = userCalendar.timeZone

    let date = dateFormatter.date(from: UserDefaults.standard.array(forKey: "dates")![index] as! String)!

    let timeLeft = userCalendar.dateComponents([.day, .hour, .minute, .second], from: currentDate, to: date)

    countdownLabel.text = "\(timeLeft.day!) days \(timeLeft.hour!) hours \(timeLeft.minute!) minutes \(timeLeft.second!) seconds"

    if currentDate >= date {

        countdownLabel.text = "Cheers! This event has occured already."
        print("An event expired.")

    }

}

按照@Satyen 的建议,由这个 var 索引触发:

var index: Int = 0 {
    didSet {
        
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(UpdateTimeLabel), userInfo: nil, repeats: true)
        timer.fire()
        print("timer fired")
        
    }
}

现在一切正常!我仍然有一些效率调整来使我的代码更简单,但总的来说它现在显示剩余时间并每秒减少一次!好激动!!

再次感谢@Satyen 的宝贵意见!