Swift CountDown Days Until from Date Picker - ios

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."
}

Related

How to countdown time for every islamic prayer

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))

I have Start Date Time And End Date Time How To Show End Sale in iOS Swift 4

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()
}
}

Displaying Live Time Label in Swift 4?

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())
}

UIDatePicker with 5 min interval get date on appear with interval

I have spent about 3 hours trying to get a UIDatePicker to do what I want it to do. I have it setup as an action of a UITextField to segue to a new view controller with a UIDatePicker. This is all working. I have been able to select a date and send it back to the original view controller.
The problem is that I am using 5 minute time intervals. If I don't move the date picker "wheels" and select my done button to return to the parent controller the non-interval time is returned. So 11:37 returns 11:37 when I need 11:40. However, if I change the "wheels" it returns the rounded time. The datepicker is currently showing the 5 minute intervals on appear. Any clues to lead me to the right solution? I have tried setDate and minuteInterval on the UIDatePicker on viewDidAppear to no avail. Below is the code I have been using:
protocol DatePickerDelegate {
func datePickerDidSelect(selectedDate: String)
}
class DatePickerVC: UIViewController {
#IBOutlet weak var pickerView: UIDatePicker!
var selectedDate = ""
var delegate : DatePickerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
//setDate()
//Have tried using setDate and minuteInterval here
}
func formatDate() -> DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM d, yyyy h:mm a"
return dateFormatter
}
func setDate() {
let dateFormatter = formatDate()
selectedDate = dateFormatter.string(from: pickerView.date)
self.delegate?.datePickerDidSelect(selectedDate: selectedDate)
print(selectedDate)
}
#IBAction func datePickerChanged(_ sender: Any) {
setDate()
}
#IBAction func doneBtnPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func cancelBtnPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
you could check the date and round it to your needs like this:
override func viewDidLoad() {
super.viewDidLoad()
let calendar = Calendar.current
var dateComponents = calendar.dateComponents([.month, .day, .year, .hour, .minute], from: datePicker.date)
guard var hour = dateComponents.hour, var minute = dateComponents.minute else {
print("something went wrong")
return
}
let intervalRemainder = minute % datePicker.minuteInterval
if intervalRemainder > 0 {
// need to correct the date
minute += datePicker.minuteInterval - intervalRemainder
if minute >= 60 {
hour += 1
minute -= 60
}
// update datecomponents
dateComponents.hour = hour
dateComponents.minute = minute
// get the corrected date
guard let roundedDate = calendar.date(from: dateComponents) else {
print("something went wrong")
return
}
// update the datepicker
datePicker.date = roundedDate
}
}
feel free to ask if anything is unclear!
Another alternative is to create an extension that allows you to tweak intervals on the date value that's set when the UIDatePicker is initialized.
extension Date {
var nearest30Min: Date {
var dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: self)
guard let hours = dateComponents.hour else { return self }
switch dateComponents.minute ?? 0 {
case 0...14:
dateComponents.minute = 0
case 15...44:
dateComponents.minute = 30
case 44...59:
dateComponents.minute = 0
dateComponents.hour = hours + 1
default:
break
}
return dateComponents.date ?? self
}
}
After your UIDatePicker is initialized, you could update the datePicker's value with the following code:
datePicker.setDate(datePicker.date.nearest30min, animated: false)

How do I get the dates for a whole week?

I am trying to get the dates for the whole week. I can currently get the current date, but I want to add 6 days to that so I can get a list of the whole week. How would I do that? Any help is appreciated!
let now = NSDate()
override func viewDidLoad() {
super.viewDidLoad()
print("Now \(now)") //prints current date
}
A quick and easy way of doing it:
if let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) {
var thisWeek = [NSDate]()
for i in 0...6 {
if let nextDay = calendar.dateByAddingUnit(NSCalendarUnit.Day , value: i, toDate: NSDate(), options: .MatchStrictly) {
thisWeek.append(nextDay)
}
}
}

Resources