Group Group Group Group Group Group Group Group Group

Make scheduled timer call on the start of the hour

I want my swift code to call the selector at the beginning of every hour. So 1 pm 2pm 4pm etc. Every time it is the beginning of the hour the code should call the function tick. Every hour the background color of the label should alternate colors. I have added a var for a current date and a function to convert the time to the military time.

import UIKit

class ViewController: UIViewController {
    var box = UILabel()
    
    var timer = Timer()
    
    var currentDateTime = Date()
    
    lazy var dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "hh:mm" // or "hh:mm a" if you need to have am or pm symbols
        return formatter
    }()
    
 
    
    override func viewDidLoad() {
        super.viewDidLoad()
   //somehow make timer inertval on the hour
        timer = Timer.scheduledTimer(timeInterval: 3600.0, target: self, selector:#selector(self.tick) , userInfo: nil, repeats: true)
        
        box.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(box)
        box.backgroundColor = .red
        
        
        NSLayoutConstraint.activate([
            
            
            
            box.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
            box.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
            box.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            box.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            
        ])
        
        
        
    }
    
    var count = 0
    @objc func tick() {
        if count % 2 == 0 {
            
            box.backgroundColor = .cyan
           count +=1
        }
        else {
            box.backgroundColor = .red
         count +=1
        }
        

        
    }
    
    func getMinutesFromDate(date: Date) -> Int {
        let dateComponents = Calendar.current.dateComponents([.hour, .minute], from: date)
        let minutes = ((dateComponents.hour ?? 0) * 60) + (dateComponents.minute ?? 0)
        return minutes
    }
}
1 Like

Instead of count += 1
I’d use a True / False toggle approach
if count == True {

        box.backgroundColor = .cyan
       count = False
    }
    else {
        box.backgroundColor = .red
     count = True
    } 

It has the advantage of not over flowing the counters upper limit.

Thanks for your response. I dont know how to set timer to stop/ start again at the start of the hour in like a endless loop.

Humm , Oh sorry! Thought you knew how to use the timer? I don’t :roll_eyes:
I was going to use your example to try and build am event timer, myself. :slightly_smiling_face:

I would think if you get a timer running with minutes and seconds displaying you could then use it to experiment at taking an action when every minute or few seconds have passed.?.

Sorry I am not much help with the timer at the moment ! :worried:

Hi @timswift , I am very far from an expert but here is the way I would have go

import UIKit

class ViewController: UIViewController {

var box = UILabel()

// As stated by @catzshadowy for your sample with 2 background states
// I tend to prefer a boolean state instead of the modulo of a number.
// I am using the property observer of boxState to change the label color
// I have no clue about efficiency but I like very much property observers,
// because it seems cleaner and easiest to read at least in my mind
var boxState: Bool? {
 didSet {
  box.backgroundColor = boxState! ? .red : .blue
 }
}

// keep a reference to timer
var timer: Timer?

override func viewDidLoad()
{
 // init boxState to give the first red backgroundColor
 boxState = true

 // copy/paste of your sample here
 box.translatesAutoresizingMaskIntoConstraints = false
 view.addSubview(box)

 NSLayoutConstraint.activate([
  box.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
  box.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
  box.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  box.centerYAnchor.constraint(equalTo: view.centerYAnchor)
 ])

 super.viewDidLoad()

 // Start timer now
 startTimer()
}

func startTimer()
{
 // not really useful in this sample, but I tend to clear stuff before setting it
 stopTimer()

 // get current date components
 var components = Calendar.current.dateComponents([ .year, .month, .day, .hour, .minute, .second, .timeZone ], from: Date())
 // update components to define next hour:00:00 from now
 components.hour = (components.hour ?? 0) + 1
 components.minute = 0
 components.second = 0
 // set repeat interval to 3600 seconds
 let interval:TimeInterval = 3600

 // for my tests I used a 5 seconds timer, because it was too long to wait for a full hour :p
 // let interval:TimeInterval = 5
 // components.second = ( components.second ?? 0) + 5

// create the next update date from components
 if let date = Calendar.current.date(from: components)
 {
  // define the timer from next date repeated every set interval
  // I am using escaping closure here because I really hate @objc stuff
  timer = Timer(fire: date, interval: interval, repeats: true) {
   [weak self] _ in
   guard let self = self else { return }
   // toggle box state
   self.boxState = !self.boxState!
  }
  // start the never ending loop timer
  RunLoop.main.add(timer!, forMode: .common)
 }
}

func stopTimer()
{
 // get confident the timer is not nil
 guard let timer = timer else { return }
 // stop it
 timer.invalidate()
}


}

Hope it helps.