【问题标题】:Setting Timer Countdown From When Object was Created从创建对象开始设置计时器倒计时
【发布时间】:2015-10-29 14:39:19
【问题描述】:

我正在 Parse 中的多个对象中查询 createdAt 日期。我想创建一个 24 小时(或 48/72 等)倒计时计时器,该计时器从创建对象到 24 小时后倒计时。 (我也在格式化它,并在单元格 UILabel 上显示它。)

示例:如果一个对象是在 19:34:33 创建的,我希望它在 24 小时后过期(或者我在创建后指定多少小时)。最终在 UILabel 上显示距离对象过期的时间。

目前,我正在检索它的创建时间,使其从创建时间开始重复倒计时。

但是,我想制定逻辑,以便在创建对象时需要它,然后显示在 24 小时或 48 小时、72 小时等结束之前还剩多少小时。

编辑

感谢@pulsar,我在下面的描述中添加了几行代码。现在的问题是我只能检索并正确倒计时 1 个对象的 createdAt 日期和时间,因为 仅 查询第一个对象,使所有其他对象都具有在它们各自的 indexPath.row 中与 Parse 中的第一个对象相同的过期 countDown 计时器。

我不知道如何添加所有对象,以便它们都有自己的由 expiresAt 函数触发的 countDown 过期时间。

以下是我查询和格式化它的方式(在 viewDidLoad 中): 这是我问的帮助我格式化日期的问题:Swift countDown timer Logic

请看代码中的 cmets!

            var createdAt = object.createdAt
            if createdAt != nil {


                 //assuming this is where i have to set expiration logic?

                let calendar = NSCalendar.currentCalendar()
                let comps = calendar.components([.Hour, .Minute, .Second], fromDate: createdAt as NSDate!)
                let hours = comps.hour  * 3600
                let minutes = comps.minute * 60
                let seconds = comps.second

                 //I'm adding these two lines below but not sure what to do with them considering I need to add all the objects to an array that will then display it on indexPath.row(s)

                   let twentyFourHours = NSTimeInterval(60 * 60 * 24)
                    self.expiresAt = NSDate(timeInterval: twentyFourHours, sinceDate: object.createdAt!!)


                self.timerInt.append(hours + minutes + seconds)
                //What do i append to the timerInt array? How can i append the objects while triggering the the expiresAt function? 

           } 

这是我的倒计时功能:

               func countDown() {

        //timerInt is an array where I'm storing the Int values.
        for i in 0 ..< timerInt.count {

            let hours = timerInt[i] / 3600 
          //have to somehow add the expiresAt method while looping through each value [i]...?
            let minsec = timerInt[i] % 3600
            let minutes = minsec / 60
            let seconds = minsec % 60
       print(String(format: "%02d:%02d:%02d", hours, minutes, seconds))
          timerInt[i]--

           //Im assuming best practice would be to loop through the values in order to change the values/set the expiration time to each object (the expiresAt method). Any ideas of how and where I can add this in this loop so that it reflects the countDown I want to set?

        }

        self.tableView.reloadData()

}

最后,对于我的 indexPath.row,我将其格式化并显示如下:

          override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
         let myCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! cell

  //I'm formatting the hours, minutes, seconds. However I'm missing the expiresAt function and I have no clue as to where and how to include it... Should it be here or in the countDown() loop?  

     let hours = timerInt[indexPath.row] / 3600
     let minsec = timerInt[indexPath.row] % 3600
     let minutes = minsec / 60
     let seconds = minsec % 60


      myCell.secondLabel.text = String(format: "%02d:%02d:%02d", hours, minutes, seconds)


        return myCell
}

关于如何将其设置为从创建之日起 24/48/72 小时后倒计时的想法?

非常感谢任何和所有帮助!

【问题讨论】:

    标签: swift nsdate swift2 countdown


    【解决方案1】:

    听起来您需要设置到期日期,然后获取当前日期和到期日期之间的日期组件。然后您可以使用NSTimer 来刷新显示。 (完成后别忘了致电NSTimer.invalidate())。

    一个例子:

    class YourViewController: UIViewController {
    
        var expiresAt: NSDate?
    
        func viewDidLoad() {
            // your parse logic here
            let twentyFourHours = NSTimeInterval(60 * 60 * 24)
            expiresAt = NSDate(timeInterval: twentyFourHours, sinceDate: createdAt)
            scheduleTimer()
        }
    
        func scheduleTimer() {
            NSTimer.scheduledTimerWithTimeInterval(1.0 / 30.0, target: self, selector: "tick:", userInfo: nil, repeats: true)
        }
    
        @objc
        func tick(timer: NSTimer) {
            guard let expiresAt = expiresAt else {
                return
            }
            let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
            if let components = calendar?.components([.Hour, .Minute, .Second], fromDate: NSDate(), toDate: expiresAt, options: []) {
                print(formatDateComponents(components))
            }
        }
    
        func formatDateComponents(components: NSDateComponents) -> String {
            let hours = components.hour
            let minutes = components.minute 
            let seconds = components.second
            return "\(hours):\(minutes):\(seconds)"
        }
    
    }
    

    您还可以通过使用结构来存储日期组件而不是对 timerInt 字符串进行复杂的解析,从而使您的生活更轻松。

    struct Time {
        let hours: String
        let minutes: String
        let seconds: String
    }
    

    【讨论】:

    • 感谢您的回答。我应该把这些功能放在哪里?在 viewDidLoad/我在哪里查询?在课堂上?我该如何处理现有代码?
    • 您可以根据需要将方法复制并粘贴到您的视图控制器中。你只需要在调用scheduleTimer()之前设置expiresAt日期。
    • 我对 Swift 比较陌生,理解代码有困难。有没有一种方法可以更改上面的函数,我从 Parse 查询数据(第一个代码 sn-p),然后使用 Parse 中的 createdAt 列从那里倒计时?
    • createdAt 在你解决了expiresAt 之后就不再重要了。我的建议是为expiresAt(在您的方法之外)创建一个实例变量,并将其设置为您从 Parse 查询数据的位置。设置完成后,您可以调用scheduleTimer(),每个tick 都使用expiriesAt 和当前日期来计算还剩多少时间。这有帮助吗?
    • 我如何将expiresAt 变量设置为我从 Parse 查询数据的位置?感谢您的耐心等待。
    【解决方案2】:
    //: Playground - noun: a place where people can play
    
    import XCPlayground
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
    import UIKit
    
    class MyView: UIView {
        weak var l1: UILabel?
        weak var l2: UILabel?
        weak var l3: UILabel?
    
        let validFor: NSTimeInterval
        var validTo: NSDate = NSDate()
    
        lazy var timer: NSTimer = NSTimer(timeInterval: self.validFor, target: self, selector: "done", userInfo: nil, repeats: false)
    
        init(validFor: NSTimeInterval) {
            self.validFor = validFor
            super.init(frame: CGRect(x: 0, y: 0, width: 500, height: 100))
            validTo = timer.fireDate
    
            let ll1 = UILabel(frame: CGRect(x: 1, y: 1, width: 498, height: 30))
            ll1.text = "created at: \(NSDate())"
            self.addSubview(ll1)
            l1 = ll1
    
            let ll2 = UILabel(frame: CGRect(x: 1, y: 33, width: 498, height: 30))
            ll2.text = "valid to: \(validTo)"
            self.addSubview(ll2)
            l2 = ll2
    
            let ll3 = UILabel(frame: CGRect(x: 1, y: 66, width: 498, height: 30))
            ll3.text = "valid for next: \(validTo.timeIntervalSinceNow) second"
            self.addSubview(ll3)
            l3 = ll3
    
            NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        // when MyView expires it trigers this function
        // and give me a chance to update UI
        func done() {
            // update UI
            dispatch_async(dispatch_get_main_queue(), { [unowned self] () -> Void in
                self.l2?.text = "     EXPIRED"
                self.l3?.text = ""
                if let text1 = self.l1?.text,
                    let text2 = self.l2?.text,
                    let text3 = self.l3?.text
                {
                    print("")
                    print(text1, text2, text3)
    
                }
            })
        }
        func updateState() {
            // periodically updating UI on request
            if timer.valid {
                let v = validTo.timeIntervalSinceNow
    
                // update UI
                dispatch_async(dispatch_get_main_queue(), { [unowned self] () -> Void in
                    self.l3?.text = "valid for next: \(v) second"
                    if let text1 = self.l1?.text,
                        let text2 = self.l2?.text,
                        let text3 = self.l3?.text
                    {
    
                        print(text1, text2, text3)
    
    
                    }
                })
            }
        }
    }
    
    
    let c = MyView(validFor: 10.0) // in seconds
    
    let queue = dispatch_queue_create("update", DISPATCH_QUEUE_SERIAL)
    
    // periodic action tu update UI
    // in regular intervals
    // this is just for demonstration, avoid using use sleep
    // in real application
    dispatch_async(queue) { () -> Void in
        repeat {
            c.updateState()
            sleep(3)
        } while true
    }
    
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        print("the app is running and responding on user actions")
        print("MyView labels are updating 'automatically'\n")
    })
    
    print("playground continue ....\n")
    

    【讨论】:

    • 感谢您的回答。如何将其集成到我的 VC 中?
    • 现在每个对象都有自己的生命,因此在您的 UI 更新周期中,只需使用您自己的 alive() 版本,它就可以满足您的要求。
    • 我的主要问题是将createdAt 变量存储在一个数组中,以便访问它们并根据indexPath.row 进行UI 更新。我无法理解您的代码。你介意添加解释吗?这纯粹是为了比较 NSDates 吗? postimg.org/image/mp1g8frpr 这就是我得到的。无效日期和 createdAt 日期相同。一个解释会很棒:)谢谢!
    • 我更新了示例。在你的操场上运行它。检查,用户界面如何“自动”更新。将代码视为向您展示想法的非常简化的基础。
    • 我不知道如何在我的 VC 中实现这一点。它过于复杂。你在一个项目中试过这个吗?我基本上需要将数组中的许多日期与 expiresAt 函数进行比较。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2019-07-10
    相关资源
    最近更新 更多