сбросить дату на следующий час после достижения максимума часа - PullRequest
0 голосов
/ 11 июля 2020

Я хочу, чтобы мой быстрый код вел обратный отсчет до ближайшего максимума часа. Итак, если время 146, код пользователя должен отсчитывать 14 минут. Прямо сейчас мой код ниже ведет обратный отсчет до определенного c дня и времени. Я просто хочу, чтобы он отсчитывал до ближайшего часа, когда приложение запущено.

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:

Моя цель в моем быстром коде - когда начало часа достиг. После попытки реализовать ответ @Leo он печатает "Top of Hour", и проблема в том, что он делает это только один раз. Пока приложение открыто, я хочу, чтобы оно печатало «Начало часа» каждый час. Поэтому мне нужно сбросить дату окончания, что я пытался сделать на

let date = Date () end = date.nextHour

Это не позволяет компиляция кода. Поэтому мне нужно сбросить конечную переменную на следующий час.

1 Ответ

0 голосов
/ 11 июля 2020

Нет необходимости обновлять пользовательский интерфейс 10 раз в секунду. Поскольку он разряжает аккумулятор устройства намного быстрее, чем необходимо, он должен работать только раз в минуту. Вы можете изменить timeInterval таймера на 1 секунду и запланировать его срабатывание в следующую четную секунду. Чтобы получить следующий четный час и следующую четную минуту, вы можете использовать метод Calendar

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

Просто создайте два вычисленных свойства, расширяющих 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)
    }
}
...