首页 > 解决方案 > 如何在 swift 中为实时倒数计时器添加时间

问题描述

我对 swift 相当陌生,并且正在尝试拥有一个实时倒数计时器,您可以通过按下按钮来增加时间。我在另一个 StackOverflow 帖子中找到了计时器的原始代码:

@objc func UpdateTime() {
        let userCalendar = Calendar.current
        // Set Current Date
        let date = Date()
        let components = userCalendar.dateComponents([.hour, .minute, .month, .year, .day, .second], from: date)
        let currentDate = userCalendar.date(from: components)!
        
        // Set Event Date
        var eventDateComponents = DateComponents()
        eventDateComponents.year = 2020
        eventDateComponents.month = 09
        eventDateComponents.day = 20
        eventDateComponents.hour = 00
        eventDateComponents.minute = 00
        eventDateComponents.second = 00
        eventDateComponents.timeZone = TimeZone(abbreviation: "GMT")
        
        // Convert eventDateComponents to the user's calendar
        let eventDate = userCalendar.date(from: eventDateComponents)!
        
        // Change the seconds to days, hours, minutes and seconds
        let timeLeft = userCalendar.dateComponents([.day, .hour, .minute, .second], from: currentDate, to: eventDate)
        
        // Display Countdown
        daysCount.text = "\(timeLeft.day!)d \(timeLeft.hour!)h \(timeLeft.minute!)m \(timeLeft.second!)s"
        
        // Show diffrent text when the event has passed
        endEvent(currentdate: currentDate, eventdate: eventDate)
       
    }
    
    func endEvent(currentdate: Date, eventdate: Date) {
        if currentdate >= eventdate {
            daysCount.text = "0d 0h 0m 0s"
            // Stop Timer
            timer.invalidate()
        }
    }

我有一个按钮的 IBAction,理想情况下你会按下它,它会为计时器增加 30 天。我试图做我在另一篇文章中发现的类似的事情,但无法让它工作。

let date = startDate.addingTimeInterval(5 * 60)

我真的很感激这里的任何指导。谢谢。

标签: swift

解决方案


您需要的第一件事是某种“目标值”,您可以轻松更新它,但您的持续时间和格式代码也可以参考

var targetDate: Date = Date()

因为我想更新这个值,所以我给了它一个初始/默认值。您将需要将其更新为更有用的东西,也许viewDidLoad您可​​以执行类似...

let calendar = Calendar.current
targetDate = calendar.date(byAdding: .day, value: 7, to: Date())!

理想情况下,这将是一些自包含模型的一部分,但只是步骤。

接下来,我们需要一些方法来更新 UI

var timer: Timer?

//...    

let timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerTicked(_:)), userInfo: nil, repeats: true)
self.timer = timer

好的,所以,这设置了一个每半秒计时一次的计时器,它允许我们更新 UI。

为此,我实际上使用了两个函数,原因是我可以根据需要独立于计时器格式化标签

@objc func timerTicked(_ timer: Timer) {
    formatDuration(from: Date(), to: targetDate)
}

func formatDuration(from: Date, to: Date) {
    let text = durationFormatter.string(from: to.timeIntervalSince(from))
    label.text = text
}

现在,我使用 aDurationFormatter来格式化输出,因为它提供了许多额外的灵活性、选项以及最重要的本地化。

lazy var durationFormatter: DateComponentsFormatter = {
    let formatter = DateComponentsFormatter()
    formatter.allowedUnits = [.day, .hour, .minute, .second]
    formatter.unitsStyle = .brief
    
    return formatter
}()

好的,所以,这为我们提供了倒数计时器视图控制器的基本概念(我们可以轻松修改,以便目标日期实际上来自某种可插入模型,以便我们可以重用视图,但我会留下那个给你)。

现在,最后一部分,如何“获得更多时间”。答案是,你实际上已经看到了,就在顶部,我初始化targetDate.

日期/时间操作并不总是像“只需添加几秒钟”那么简单,涉及很多规则(一团糟)。

因此,在这种情况下,我们使用 a Calendar,除了为我们处理所有“肮脏”的工作之外,它也更容易阅读和理解。

@IBAction func needMoreTime(_ sender: Any) {
    // A list of date components we're willing to change
    let components: [Calendar.Component] = [.day, .hour, .minute]
    // A random amount of time to be added to a random date component
    let randomValue = Int.random(in: 1...10)
    // The component we're going to modify
    let component = components.randomElement() ?? .minute
    
    // Grab a calendar instance
    let calendar = Calendar.current
    // Update the target date
    targetDate = calendar.date(byAdding: component, value: randomValue, to: targetDate)!
}

本质上,此方法将随机时间量添加到日期的随机部分,但您可以轻松修改它以执行您想要的任何操作。

完整的例子...

而且,因为随机的、断章取义的代码并不总是能理解,这是我为测试我的理论而编写的基本示例......

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var label: UILabel!
    
    var targetDate: Date = Date()
    var timer: Timer?
    
    lazy var durationFormatter: DateComponentsFormatter = {
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.day, .hour, .minute, .second]
        formatter.unitsStyle = .brief
        
        return formatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let calendar = Calendar.current
        targetDate = calendar.date(byAdding: .day, value: 7, to: Date())!

        formatDuration(from: Date(), to: targetDate)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        formatDuration(from: Date(), to: targetDate)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        // Personal preference
        let timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerTicked(_:)), userInfo: nil, repeats: true)
        self.timer = timer
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        timer?.invalidate()
        timer = nil
    }

    @objc func timerTicked(_ timer: Timer) {
        formatDuration(from: Date(), to: targetDate)
    }
    
    func formatDuration(from: Date, to: Date) {
        let text = durationFormatter.string(from: to.timeIntervalSince(from))
        label.text = text
    }

    @IBAction func needMoreTime(_ sender: Any) {
        // A list of date components we're willing to change
        let components: [Calendar.Component] = [.day, .hour, .minute]
        // A random amount of time to be added to a random date component
        let randomValue = Int.random(in: 1...10)
        // The component we're going to modify
        let component = components.randomElement() ?? .minute
        
        // Grab a calendar instance
        let calendar = Calendar.current
        // Update the target date
        targetDate = calendar.date(byAdding: component, value: randomValue, to: targetDate)!
    }
}

推荐阅读