I make my iOS app islamic prayer, so I need to View countdown time next prayer.
this is code from Adhan project:
so I have 5 prayer for every day, and I need countdown time between every prayer.
func formattedPrayerTime(prayer: Prayer, times: PrayerTimes?) -> some View {
guard let time = times?.time(for: prayer) else {
return Text("-")
}
return Text("\(time, formatter: dateFormatter)")
}
func formattedPrayerName(prayer: Prayer) -> some View {
switch prayer {
case .fajr:
return Text("Fajr")
case .sunrise:
return Text("Sunrise")
case .dhuhr:
return Text("Dhuhr")
case .asr:
return Text("Asr")
case .maghrib:
return Text("Maghrib")
case .isha:
return Text("Isha")
}
}
}
I use something like this before
in viewdidload in releasedate prayer time and add target for #objc func
update time func will automatically will updated if time changed every second ( timeInterval ) in scheduledTimer
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let releaseDateString = "7:07:43" // <- prayer time
let releaseDateFormatter = DateFormatter()
releaseDateFormatter.dateFormat = "HH:mm:ss"
releaseDate = releaseDateFormatter.date(from: releaseDateString)! as NSDate
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#objc func updateTime() {
let currentDate = Date()
let calendar = Calendar.current
let diffDateComponents = calendar.dateComponents([.hour, .minute, .second], from: currentDate, to: releaseDate! as Date)
DispatchQueue.global().sync
{
hours.text = "\(diffDateComponents.hour ?? 0)"
min.text = "\(diffDateComponents.minute ?? 0)"
sec.text = "\(diffDateComponents.second ?? 0)"
}
you can use SwiftDate also it's awesome library
You could use this function:
func timeUntilNextPrayer(_ nextPrayer: Date) {
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
let difference = Calendar.current.dateComponents([.hour, .minute, .second], from: Date(), to: nextPrayer)
print(difference)
if nextPrayer == Date() {
timer.invalidate()
}
}
}
Call this function by passing the next prayer time as parameter. Example:
timeUntilNextPrayer(Date().addingTimeInterval(50))
Related
I have start date and end date strings, how to show the end Sale timer like this image?
"Start-date":"Dec 18, 2019 05:15:00 +0000","End-date":"Dec 27, 2019 11:15:39 +0000"
Please help!
You should convert the date string to Date first, then you can use timer to update the timer labels based on start and end date.
Use Calendar and dateComponents method to find the day, hour, minute and second differences between dates and then set the value on day, hour, minute and second labels as like you attached image.
Example implementation:
var timer:Timer?
var endDate:Date?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startTimer()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopTimer()
}
func startTimer(){
let endDateStr = "Dec 27, 2019 11:15:39 +0000"
let dateFormat = "MMM d, yyyy HH:mm:ss Z"
let dateFormater = DateFormatter()
dateFormater.dateFormat = dateFormat
endDate = dateFormater.date(from: endDateStr)
//stop timer if it's already running
stopTimer()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateSaleTime), userInfo: nil, repeats: true)
}
func stopTimer(){
if timer != nil{
timer!.invalidate()
timer = nil
}
}
func updateSaleTime(){
guard let d2 = endDate else {
stopTimer()//Check if the date-format is correct for end date string.
return
}
let cal = Calendar.current
let components = cal.dateComponents([.day, .hour, .minute, .second], from: Date(), to: d2)
let day = components.day!
let hour = components.hour!
let minute = components.minute!
let second = components.second!
//set the value on day, hour, minute and second labels as like you attached image.
}
If you calculate the difference between the startDate and the endDate it will alway be the same . Instead you can calculate the difference to endDate from the current date.
Check the following implementation.
class ViewController: UIViewController {
let endDate : Date? = {
// To create Date from date string received from server
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd, yyyy HH:mm:ss Z"
// Convert to desired Timezone
return dateFormatter.date(from: "Dec 18, 2019 06:30:39 +0000")
}()
var timer : Timer?
private func starCountDown() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
private func stopCountDown() {
timer?.invalidate()
}
#objc func updateTime() {
guard let endDate = endDate else {
stopCountDown()
return
}
let countdown = Calendar.current.dateComponents([.day, .hour, .minute, .second], from: Date(), to: endDate)
let days = countdown.day!
let hours = countdown.hour!
let minutes = countdown.minute!
let seconds = countdown.second!
if days <= 0 && hours <= 0 && minutes <= 0 && seconds <= 0 {
stopCountDown()
print("Offer Expired")
return
}
print(String(format: "%02d Days , %02d Hours, %02d Mins, %02d Sec", days, hours, minutes, seconds))
// set values as per your requirement
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
stopCountDown()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
starCountDown()
}
}
Once my button is pressed I would like to disable my button for 24 hours and displaying a countdown on a label display a countdown until the button will be active again.
I have saved the waiting date and compared it to current date but I am not sure how to display the countdown of how much time is left in hours, minutes, and seconds.
let todaysDate = Date()
func set24HrTimer() {
let currentDate = Date()
let newDate = Date(timeInterval: 86400, since: currentDate as Date)
UserDefaults.standard.set(newDate, forKey: "waitingDate")
print("24 hours started")
//disable the button
}
if let waitingDate:Date = UserDefaults.standard.value(forKey: "waitingDate") as? Date {
if (todaysDate.compare(waitingDate as Date) == ComparisonResult.orderedDescending) {
print("show button")
}
else {
print("hide button")
}
}
You can add following code to create time countdown.
First add two variable ass follow:
fileprivate var timeWorking : Bool = false // To check is timer already scheduled
var timer:Timer? // Instance of timer
Then add following code which will calculate remaining hour, minute and second.
func timeLeftExtended(date:Date) ->NSAttributedString{
let cal = Calendar.current
let now = Date()
let calendarUnits:NSCalendar.Unit = [NSCalendar.Unit.hour, NSCalendar.Unit.minute, NSCalendar.Unit.second]
let components = (cal as NSCalendar).components(calendarUnits, from: now, to: date, options: [])
let fullCountDownStr = "\(components.hour!.description)h " + "\(components.minute!.description)m " + "\(components.second!.description)s "
let mutableStr = NSMutableAttributedString(string: fullCountDownStr, attributes: [NSAttributedString.Key.foregroundColor:UIColor.white])
for (index, char) in mutableStr.string.enumerated()
{
if(char == "h" || char == "m" || char == "s")
{
mutableStr.removeAttribute(NSAttributedString.Key.foregroundColor, range: NSMakeRange(index, 1))
mutableStr.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.lightGray], range: NSMakeRange(index, 1))
}
}
return mutableStr
}
Next, add code which is scheduledTimer if not scheduled.
func setupTimer()
{
if(!timeWorking)
{
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateCountDown), userInfo: nil, repeats: true)
self.timeWorking = true
}
}
Add code to display count down in label.
#objc func updateCountDown()
{
if let waitingDate = UserDefaults.standard.value(forKey: "waitingDate") as? Date {
self.labelCountDown.attributedText = self.timeLeftExtended(date: waitingDate)
} else {
let newDate = Calendar.current.date(byAdding: .hour, value: 24, to: Date())
UserDefaults.standard.set(newDate, forKey: "waitingDate")
self.labelCountDown.attributedText = self.timeLeftExtended(date: newDate!)
}
}
Call setupTimer() method to continue timer.
Output:
My time label is displaying the time, when I open my app but it won't update it live. I had a look at other answers but they didn't make sense.
// CURRENT TIME
#IBOutlet weak var currentTimeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
getCurrentTime()
}
// FORMAT TIME
func getCurrentTime(){
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm"
let str = formatter.string(from: Date())
currentTimeLabel.text = str
}
I want my app to update the time label live. Thanks in advance. It's probably a really simple fix.
Use Timer for your requirement,
class ViewController: UIViewController {
#IBOutlet weak var currentTimeLabel: UILabel!
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
getCurrentTime()
}
private func getCurrentTime() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:#selector(self.currentTime) , userInfo: nil, repeats: true)
}
#objc func currentTime() {
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm"
currentTimeLabel.text = formatter.string(from: Date())
}
}
You can create Timer with repeating every minute (because you don't need seconds for anything) starting in the next minute (so call getCurrentTime() once before you start Timer).
Every minute code inside timer's closure gets executed so you can say that you want to call getCurrentTime(). Now your currentTimeLabel will be updated every minute
let now = Date()
let date = Calendar.current.date(bySettingHour: Calendar.current.component(.hour, from: now), minute: Calendar.current.component(.minute, from: now) + 1, second: 0, of: now)!
let timer = Timer(fire: date, timeInterval: 60, repeats: true) { _ in
self.getCurrentTime()
}
Also I would recommend you to have formatter variable outside of the method (in global scope)
lazy var formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm" // or "hh:mm a" if you need to have am or pm symbols
return formatter
}()
and then in getCurrentTime() just get String and change text of currentTimeLabel
func getCurrentTime() {
currentTimeLabel.text = formatter.string(from: Date())
}
I have been struggling making a countdown in Swift where it shows only the days left until some date where the input is the DatePicker... I have creo experience with Swift so, I have been struggling for a while. I tried some similar answers here but didn't work, I watched a tutorial but is a normal countdown with months, days, minutes and seconds, this is the code.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
let formatter = DateFormatter()
let userCleander = Calendar.current;
let requestedComponent : Set<Calendar.Component> = [
Calendar.Component.month,
Calendar.Component.day
]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timePrinter), userInfo: nil, repeats: true)
timer.fire()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func timeCalculator(dateFormat: String, endTime: String, startTime: Date = Date()) -> DateComponents {
formatter.dateFormat = dateFormat
let _startTime = startTime
let _endTime = formatter.date(from: endTime)
let timeDifference = userCleander.dateComponents(requestedComponent, from: _startTime, to: _endTime!)
return timeDifference
}
func timePrinter() -> Void {
let time = timeCalculator(dateFormat: "MM/dd/yyyy a", endTime: "12/25/2018 a")
timeLabel.text = "\(time.month!) Months \(time.day!) Days"
}
}
Several things: Don't use strings to compare dates. Use Date objects and Calendar operations. (More on that in a second.)
Don't run a timer once a second. Save the current date to user defaults. When your app is launched, compare the saved date to the current date and see if the day has changed.
When running, listen for UIApplicationSignificantTimeChange notifications, and when you get one, check to see if the date has changed.
As for comparing the current date to the user-selected date, you've got the right idea using dateComponents(_:from:to:), but you should pass in components of just [.day].
EDIT:
Code like this would do the trick:
override func viewDidLoad() {
super.viewDidLoad()
//Set up the date picker to pick dates, not dates & times
datePicker.datePickerMode = .date
//Force the date picker to use midnight today as it's base date and
//to pick a date at least 1 day in the future
guard let today = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date()),
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)
else {
return
}
datePicker.minimumDate = tomorrow
datePicker.date = tomorrow
}
#IBAction func datePickerChanged(_ sender: UIDatePicker) {
let future = datePicker.date
//Use midnight today as the starting date
guard let today = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date()) else { return }
//Calculate the number of days between today and the user's chosen day.
let difference = Calendar.current.dateComponents([.day], from: today, to: future)
guard let days = difference.day else { return }
let ess = days > 1 ? "s" : ""
infoLabel.text = "That date is \(days) day\(ess) away."
}
I have a list of quotes.
I want to have a label that changes every 24H to a new Quote.
How can I do this? Like how can I manage a day?
I know I could just use an API but I want to use my own quotes and I am not able to create an API on my own yet.
Supposing you have an array of quotes:
let numberOfQuotes = 3
let quotes = ["quote a", "quote b", "quote c"]
override func viewDidLoad() {
_ = Timer.scheduledTimer(timeInterval: TimeInterval(60),
target: self,
selector: #selector(self.updateQuote),
userInfo: nil,
repeats: true)
}
func updateQuote() {
let lastUpdate = UserDefaults.standard.object(forKey: "lastUpdate") as? Date
if lastUpdate != nil {
let date1:Date = Date() // Same you did before with timeNow variable
let date2: Date = Date(timeIntervalSince1970: lastUpdate)
let calender:Calendar = Calendar.current
let components: DateComponents = calender.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date1, to: date2)
// you can use components month, hour, second.... to update your message, in your case, we will day
if components.day! >= 1 {
UserDefaults.standard.set(Date(), forKey: "lastUpdate")
yourLabel.text = quotes[randomInt(0,numberOfQuotes)]
}
} else { //firstTime running
UserDefaults.standard.set(Date(), forKey: "lastUpdate")
yourLabel.text = quotes[randomInt(0,numberOfQuotes)]
}
}
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32((max - 1) - min + 1)))
}
This code is as is, by your description, it does exactly what you want.