I have a UILabel which displays my current 24 hour countdown. I would like to check if the countdown is at zero and when it is to update my UILabel. The way it is set up now if it is reached 24 hours it keeps running as negative numbers. For example -1h-55m-54s
I have tried to check if UILabel contains "-" and also if the value is less than 0 however have not got it to work.
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
}
func updateCountDown() {
if let waitingDate = UserDefaults.standard.value(forKey: "waitingDate") as? Date {
if let waitingDate = UserDefaults.standard.object(forKey: "waitingDate") as? Date,
waitingDate < Date() {
self.timeLabel.text = "Time ran out"
print("It is time for you to check in")
}
// if self.timeLeftExtended(date: waitingDate) <= 0{
//change label
}
self.timeLabel.attributedText = self.timeLeftExtended(date: waitingDate)
} else {
let newDate = Calendar.current.date(byAdding: .hour, value: 24, to: Date())
UserDefaults.standard.set(newDate, forKey: "waitingDate")
self.timeLabel.attributedText = self.timeLeftExtended(date: newDate!)
}
}
Just compare the dates
if let waitingDate = UserDefaults.standard.object(forKey: "waitingDate") as? Date,
waitingDate < Date() {
// waitingDate exists and is earlier than the current date
}
And you can create the countdown string much shorter
let components = cal.dateComponents([.hour, .minute, .second], from: now, to: date)
let fullCountDownStr = "\(components.hour!)h \(components.minute!)m \(components.second!)s "
Here is how I use UILabel to display count down timer
First, these are my variables
#IBOutlet weak var lblCountDownTime: UILabel! //label to display count down
var countDownTimer: Timer? // timer to count down
Second, this is how I display count down, hope it help
self.countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.countDownTime), userInfo: nil, repeats: true);
func countDownTime() {
let now = Date();
let calendar = Calendar.current;
let comps = calendar.dateComponents(Set<Calendar.Component>([.minute, .second]), from: now, to: self.startTime);
var strMinute = "\(comps.minute!)";
var strSecond = "\(comps.second!)";
if (comps.minute! < 10) {
strMinute = "0\(comps.minute!)";
}
if (comps.second! < 10) {
strSecond = "0\(comps.second!)";
}
if (comps.minute! <= 0 && comps.second! <= 0) {
self.countDownTimer?.invalidate();
}
else {
self.lblCountDownTime.text = "\(strMinute):\(strSecond)\"";
}
}
Related
I am working on an Enterprise App, on which I have to Fetch Steps data and show it into my application, into Staring for some days at End customer side it is working perfectly But after when End Customer start using iWatch than suddenly after some days, It suddenly stopped working, Custom app not able to get steps from the Health Kit.
func fetchStepsDataFromHeathKitForUploadReassure(completionHandler:#escaping handlerUpload) {
var startOfDay = Calendar.current.startOfDay(for: now)
if let date = HelperFunctions.userDefaultForKey(key: "StepDate") as? String{
print("date of user defaults",date)
if let dateValue = HelperFunctions.convert(time: date, fromFormate: "yyyy-MM-dd HH:mm:ss"),
let dateNowValue = HelperFunctions.convert(date: dateValue, fromFormate: "yyyy-MM-dd"),
let dateNow = HelperFunctions.convert(date: Date(), fromFormate: "yyyy-MM-dd"){
print("date of user defaults value",dateNow)
if(dateNowValue != dateNow){
startOfDay = Calendar.current.startOfDay(for: dateValue)
}
}
}
var interval = DateComponents()
interval.day = 1
let quantityType1 = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
// Create the query
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: NSDate() as Date, options: .strictEndDate)
let queryVAlue = HKStatisticsCollectionQuery(quantityType: quantityType1,
quantitySamplePredicate: predicate,
options: [.cumulativeSum,.separateBySource],
anchorDate: startOfDay,
intervalComponents: interval)
// Set the results handler
queryVAlue.initialResultsHandler = {
query, results, error in
guard let statsCollection = results else {
completionHandler([], nil)
return
}
let endDate = Date()
var array = [Any]()
var dict = [String:Any]()
print("startOfDay",startOfDay,"end date",endDate)
statsCollection.enumerateStatistics(from: startOfDay, to: endDate, with: { (statistics, stop) in
if let quantity = statistics.sumQuantity() {
let date = statistics.endDate
for sta in (statistics.sources ?? []) as [HKSource]{
print("sta",sta.bundleIdentifier) // this is bundle identifier of HKSource
print("sum quantity total",statistics.sumQuantity())
let totalpoints = statistics.sumQuantity()
var totalsteps = Int(totalpoints?.doubleValue(for: HKUnit.count()) ?? 0.0)
if sta.bundleIdentifier == "com.apple.Health"{
let val = statistics.sumQuantity(for: sta)
print("val",val)
let value = Int(val?.doubleValue(for: HKUnit.count()) ?? 0.0)
totalsteps = totalsteps - value
dict["value"] = totalsteps
break
}
dict["value"] = totalsteps
}
dict["date"] = HelperFunctions.convertToLocal(date: date, fromFormate: "dd/MM/yyyy")
array.append(dict)
}else{
let date = statistics.endDate
let value = 0
dict["value"] = value
dict["date"] = HelperFunctions.convertToLocal(date: date, fromFormate: "dd/MM/yyyy")
array.append(dict)
}
})
completionHandler(array, nil)
}
healthStore.execute(queryVAlue)
}
I would like to send a local notification once 24 hours has passed after a button is selected. The button is currently only enabled once 24 hours has passed and the waiting time is saved in UserDegaults. I would like to know how I will be able to send a local notification to the user automatically once the 24 hours has passed.
func getNextQuote(){
let defaults = UserDefaults.standard
defaults.integer(forKey: "savedIndexKey")
let currentIndex = defaults.integer(forKey: "savedIndexKey")
var nextIndex = currentIndex+1
nextIndex = quotes.indices.contains(nextIndex) ? nextIndex : 0
defaults.set(nextIndex, forKey: "savedIndexKey")
let savedInteger = defaults.integer(forKey: "savedIndexKey")
saved = savedInteger
quotesLabel.text = quotes[savedInteger]
self.quotesLabel.fadeIn()
set24HrTimer()
update()
}
func update(){
if let waitingDate = UserDefaults.standard.value(forKey: "waitingDate") as? Date{
if let waitingDate = UserDefaults.standard.object(forKey: "waitingDate") as? Date,
waitingDate < Date() {
self.timeLabel.text = "Please check in for the day"
self.remainingLabel.text = "Did you stay clean today?"
self.remainingLabel.font = UIFont(name: "Arial", size: 32)
self.quotesLabel.isHidden = true
addButton.isHidden = false
noButton.isHidden = false
gridButton.isHidden = false
self.view.setNeedsDisplay()
print("time is up")
}else if let waitingDate = UserDefaults.standard.value(forKey: "waitingDate") as? Date {
self.timeLabel.attributedText = self.timeLeftExtended(date: waitingDate)
addButton.isHidden = true
noButton.isHidden = true
gridButton.isHidden = true
self.quotesLabel.isHidden = false
self.remainingLabel.text = "Until next check in"
self.quotesLabel.fadeIn()
print("still running")
}else{
let newDate = Calendar.current.date(byAdding: .hour, value: 24, to: Date())
UserDefaults.standard.set(newDate, forKey: "waitingDate")
self.timeLabel.attributedText = self.timeLeftExtended(date: newDate!)
print("last option")
}
}
}
func set24HrTimer() {
let currentDate = Date()
let newDate = Date(timeInterval: 86400, since: currentDate as Date)
UserDefaults.standard.setValue(newDate, forKey: "waitingDate")
print("24 hours started")
}
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.dateComponents([.hour, .minute, .second], from: now, to: date)
let fullCountDownStr = "\(components.hour!)h \(components.minute!)m \(components.second!)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
}
func setupTimer()
{
if(!timeWorking)
{
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateCountDown), userInfo: nil, repeats: true)
self.timeWorking = true
}
}
You shouldn't use a timer to trigger a local notification. This function could be added as an extension to UIViewController that will let you create a UNCalendarNotificationTrigger:
import UserNotifications
import UIKit
extension UIViewController {
func createLocalNotification(title: String, hours: Int) {
let seconds = hours * 3600
let content = UNMutableNotificationContent()
content.sound = UNNotificationSound.default
content.title = title
let nextTriggerDate = Calendar.current.date(byAdding: .second, value: seconds, to: Date())!
let comps = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: nextTriggerDate)
let trigger = UNCalendarNotificationTrigger(dateMatching: comps, repeats: false)
let request = UNNotificationRequest(identifier: "\(seconds)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}
And can be used within your UIViewController as follows:
let MyViewController: UIViewController {
#IBAction func buttonPressed(_ sender: Any) {
self.createLocalNotification(title: "24 hours have passed!", hours: 24)
}
}
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:
what i am to do with the button is once it is pressed it will disable for the rest of the day. now my code does disable it once it is pressed but if the user leaves the application and comes back to it, the button will be enabled again. is there a way to use NSUserDefaults?
let save = UserDefaults.standard
let calendar = Calendar.current
let now = Date()
this is in the viewDidLoad:
let seven_today = calendar.date(
bySettingHour: 7,
minute: 0,
second: 0,
of: now)!
let two_thirty_today = calendar.date(
bySettingHour: 14,
minute: 30,
second: 0,
of: now)!
if now >= seven_today && now <= two_thirty_today
{
getPointsOutlet.isEnabled = true
}
else
{
getPointsOutlet.isEnabled = false
}
this is the function of pressing the button:
Total_Points += 12
pointsLabel.text = "Total Points: \(Total_Points)"
getPointsOutlet.isEnabled = false
Try this:
if now >= seven_today && now <= two_thirty_today
{
let savedDayNum = defaults.integer(forKey: "dayClickNum")
let date = Date()
let calendar = Calendar.current
let nowDayNum = calendar.component(.day, from: date)
if(savedDayNum == nowDayNum)
{
getPointsOutlet.isEnabled = false
}
else
{
getPointsOutlet.isEnabled = true
}
}
else
{
getPointsOutlet.isEnabled = false
}
// in function click do this
Total_Points += 12
pointsLabel.text = "Total Points: \(Total_Points)"
getPointsOutlet.isEnabled = false
let date = Date()
let calendar = Calendar.current
let dayOfClickDate = calendar.component(.day, from: date)
defaults.set(dayOfClickDate, forKey: "dayClickNum")
Also for a perfect solution you may take care of month , as app may not be launched again by user for a month that may disable the button if the stored num day is coincidence d with open day of another month
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.