达到小时数后将日期重置为下一个小时

reset date to next hour after top of hour is reached

我希望我的 swift 代码倒计时到最近的整点。所以如果时间是 146,用户代码应该倒数 14 分钟。现在我下面的代码倒计时到特定的日期和时间。我只希望它在应用程序 运行.

时倒计时到最近的小时
import UIKit

class ViewController: UIViewController {
    @IBOutlet var timerLabel: UILabel!
    
    var timer: Timer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(UpdateTime), userInfo: nil, repeats: true)
    }
    
    @objc func UpdateTime() {
        let userCalendar = Calendar.current
        // Set Current Date
        let date = Date()
        let components = userCalendar.dateComponents([.hour, .minute, .month, .year, .day], from: date)
        let currentDate = userCalendar.date(from: components)!
        
        // Set Event Date
        var eventDateComponents = DateComponents()
        eventDateComponents.year = 2021
        eventDateComponents.month = 01
        eventDateComponents.day = 01
        eventDateComponents.hour = 01
        eventDateComponents.minute = 00

        eventDateComponents.timeZone = TimeZone(abbreviation: "GMT")
        

        let eventDate = userCalendar.date(from: eventDateComponents)!
        
        let timeLeft = userCalendar.dateComponents([.day, .hour, .minute, ], from: currentDate, to: eventDate)
    
        timerLabel.text = "\(timeLeft.day!)d \(timeLeft.hour!)h \(timeLeft.minute!)m "

        endEvent(currentdate: currentDate, eventdate: eventDate)
    }
    
    func endEvent(currentdate: Date, eventdate: Date) {
        if currentdate >= eventdate {
            timerLabel.text = "Happy New Year!"
            // Stop Timer
            timer.invalidate()
        }
    }
}

edit/update:

我的 swift 代码中的目标是达到整点。在尝试执行@Leo 的回答后,它会打印“Top of Hour”,但问题是它只执行一次。只要该应用程序处于打开状态,我就希望它每小时打印一次“Top of Hour”。所以我需要重置结束日期,这是我在

尝试做的

let date = Date() end = date.nextHour

这不会让代码编译。所以我必须将结束变量重置为下一个小时。

无需每秒更新用户界面 10 次。事实上,它会比需要更快地耗尽设备的电池,而它应该只 运行 每分钟一次。您可以将计时器 timeInterval 更改为 1 秒,并安排它在下一个偶数秒触发。要获得下一个偶数小时和下一个偶数分钟,您可以使用 Calendar 方法

func nextDate(after date: Date, matching components: DateComponents, matchingPolicy: Calendar.MatchingPolicy, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward) -> Date?

只需创建两个扩展日期的计算属性并为分钟或纳秒分量传递零:

extension Date {
    var nextHour: Date {
        Calendar.current.nextDate(after: self, matching: DateComponents(minute: 0), matchingPolicy: .strict)!
    }
    var nextSecond: Date {
        Calendar.current.nextDate(after: self, matching: DateComponents(nanosecond: 0), matchingPolicy: .strict)!
    }
    var minute: Int {
        Calendar.current.component(.minute, from: self)
    }
}

现在向您的视图控制器添加一个 属性 以保留对结束日期的引用。请注意,无需将计时器声明为可选:

var end: Date?
var timer = Timer()

并创建 DateComponentsFormatter 以创建剩余时间的本地化描述:

extension Formatter {
    static let minutesRemaining: DateComponentsFormatter = {
        let formatter = DateComponentsFormatter()
        formatter.formattingContext = .standalone
        formatter.unitsStyle = .short
        formatter.allowedUnits = [.minute, .second]
        formatter.includesTimeRemainingPhrase = true
        return formatter
    }()
}

现在您只需设置结束日期并安排您的计时器在下一个偶数分钟触发:

override func viewDidLoad() {
    super.viewDidLoad()
    // get the current date
    let date = Date()
    // set the end date
    end = date.nextHour
    // schedule the timer to fire at the next even second and set its interval to 1 second
    timer = .init(fireAt: date.nextSecond, interval: 1, target: self, selector: #selector(updateUI), userInfo: nil, repeats: true)
    RunLoop.main.add(timer, forMode: .common)
    updateUI()
}
@objc func updateUI() {
    if Date().minute == 0 || Date() > end {
        end = Date().nextHour
        timerLabel.text = "beginning of hour"
        print("beginning of hour")
    } else {
        // update the remaining time (for a perfect sync we need to subtract a second from the current time)
        let text = Formatter.minutesRemaining.string(from: Date().addingTimeInterval(-1), to: end) ?? ""
        timerLabel.text = text
        print(text)
    }
}