Im using Xcode Version 11.0 (11A420a) and Swift to make an iPhone and Apple Watch app I have some code that has made a simple timer label and start and stop buttons and Id like to also have that on the Apple Watch.
In the Xcode viewcontroller.swift file I have this code and it works great.
var startTime = TimeInterval()
var startofTime = Date()
var timer:Timer = Timer()
var endTime: Date!
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var Start: UIButton!
#IBOutlet weak var Stop: UIButton!
#IBAction func startAct(_ sender: Any) {
alarmTime = Date()
startofTime = Date()
Start.isHidden = true
Stop.isHidden = false
if (!timer.isValid) {
let aSelector : Selector = #selector(ViewController.updateTime)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
startTime = Date.timeIntervalSinceReferenceDate
}
}
#IBAction func stopAction(_ sender: Any) {
progressView.progress = 0.0
progress.completedUnitCount = 1024
Start.isHidden = false
Stop.isHidden = true
endTime = Date()
timer.invalidate()
}
#objc func updateTime() {
let currentTime = Date.timeIntervalSinceReferenceDate
//Find the difference between current time and start time.
var elapsedTime: TimeInterval = currentTime - startTime
// print(elapsedTime)
// print(Int(elapsedTime))
//calculate the hours in elapsed time.
let hours = UInt8(elapsedTime / 3600.0)
elapsedTime -= (TimeInterval(hours) * 3600)
//calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (TimeInterval(minutes) * 60)
//calculate the seconds in elapsed time.
let seconds = UInt8(elapsedTime)
elapsedTime -= TimeInterval(seconds)
//find out the fraction of milliseconds to be displayed.
let fraction = UInt8(elapsedTime * 100)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let strFraction = String(format: "%02d", fraction)
//concatenate minuets, seconds and milliseconds as assign it to the UILabel
timerLabel.text = "\(hours):\(strMinutes):\(strSeconds).\(strFraction)"
}
When I tried to copy it to the watch InterfaceController.swift I got most of it done by just coping and pasting but I get a error about the label.
!Cannot assign to value: 'setText' is a method
Can anyone help me with the label for the watch. Something like this -
timerLabel.setText = "\(hours):\(strMinutes):\(strSeconds).\(strFraction)"
if I just put
timerLabel.setText((strFraction))
It works to display the milliseconds but id like to combine them all please.
If I do this
timerLabel.setText((strSeconds)(strFraction))
I get this error
!Cannot call value of non-function type 'String'
This is the full watch code so far, just need the last line
//
// InterfaceController.swift
// WatchKit Extension
//
// Created by Kurt on 3/10/19.
// Copyright © 2019 Kurt. All rights reserved.
//
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController {
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
var startTime = TimeInterval()
var startofTime = Date()
var timer:Timer = Timer()
var endTime: Date!
#IBOutlet weak var timerLabel: WKInterfaceLabel!
#IBAction func Start() {
startofTime = Date()
if (!timer.isValid) {
let aSelector : Selector = #selector(InterfaceController.updateTime)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
startTime = Date.timeIntervalSinceReferenceDate
}
}
#objc func updateTime() {
let currentTime = Date.timeIntervalSinceReferenceDate
//Find the difference between current time and start time.
var elapsedTime: TimeInterval = currentTime - startTime
// print(elapsedTime)
// print(Int(elapsedTime))
//calculate the hours in elapsed time.
let hours = UInt8(elapsedTime / 3600.0)
elapsedTime -= (TimeInterval(hours) * 3600)
//calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (TimeInterval(minutes) * 60)
//calculate the seconds in elapsed time.
let seconds = UInt8(elapsedTime)
elapsedTime -= TimeInterval(seconds)
//find out the fraction of milliseconds to be displayed.
let fraction = UInt8(elapsedTime * 100)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let strFraction = String(format: "%02d", fraction)
//concatenate minuets, seconds and milliseconds as assign it to the UILabel
timerLabel.setText(\(hours):\(strMinutes):\(strSeconds).\(strFraction)) !error
}
}
You can do it as,
timerLabel.setText("\(hours):\(strMinutes):\(strSeconds).\(strFraction)")
The setText method isn't available for the UILabel class in Swift.
Assuming you've created a setText extension method to the UILabel class,
you can do it this way:
timerLabel.setText("\(hours):\(strMinutes):\(strSeconds).\(strFraction)")
The setText method isn't available for the UILabel class in Swift. You can use text propriety of UILabel.
timerLabel.text = "\(hours):\(strMinutes):\(strSeconds).\(strFraction)"
For UILabel, text is a property & :setText is setter method for this property. So either use this property as:
timerLabel.text = "\(hours):\(strMinutes):\(strSeconds).\(strFraction)"
Or, use setter method as:
timerLabel.setText("\(hours):\(strMinutes):\(strSeconds).\(strFraction)")
But for iWatch, WKInterfaceLabel does not have any text property. You can only change the text at runtime using :setText method. For more info: see here
Related
I have searched answers in stackoverflow and none of them matches my needs. I am creating time tracking app on Swift Cocoa macOS, like Hubstaff time tracking app. At the moment runs a timer and I want to detect user's inactivity after x period of time and to send a Notification that he has been Idle x period of time. I'm new to iOS and macOS development. Can I have an example of how to do it?
Here is my code:
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var label: NSTextField!
#IBOutlet weak var playImage: NSButton!
var timer : Timer!
var isTimerWorking : Bool = false
var startTime : Date!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func playPause(_ sender: NSButton) {
if isTimerWorking {
endTimer()
playImage.image = NSImage(named: NSImage.Name("play"))
sender.state = .off
} else {
startTimer()
playImage.image = NSImage(named: NSImage.Name("stop"))
sender.state = .off
}
}
func startTimer() {
startTime = Date()
timer = Timer.scheduledTimer(
timeInterval: 1.0,
target: self,
selector: #selector(self.timerCounter),
userInfo: nil,
repeats: true
)
isTimerWorking = true
}
func endTimer() {
if timer != nil {
timer.invalidate()
label.stringValue = "00:00:00"
}
isTimerWorking = false
}
#objc func timerCounter() {
let currentTime = Date().timeIntervalSince(startTime)
let hour = Int(fmod(currentTime/3600, 60))
let minute = Int(fmod(currentTime/60, 60))
let second = Int(fmod(currentTime, 60))
let hourValue = String(format:"%02d", hour)
let minuteValue = String(format:"%02d", minute)
let secondValue = String(format:"%02d", second)
label.stringValue = "\(hourValue):\(minuteValue):\(secondValue)"
}
}
In my own time tracking app I am using
var lastEvent:CFTimeInterval = 0
lastEvent = CGEventSource.secondsSinceLastEventType(CGEventSourceStateID.hidSystemState, eventType: CGEventType(rawValue: ~0)!)
print(lastEvent)
to get the user idle time.
I developing a timer app for an apple watch.
I have two different Views at the moment. One with the actual timer (TimerController) and another with a pause-button (SwipeController).
I'm trying to stop/start the timer in the TimerController with the action from the button in the SwipeController.
Problem is that the timer stops, but the timer will not start again after hitting the button the second time.
If I press the button one time, the timer stops. If i press it again two times the timer will start again but will not stop when hitting the button again.
Any ideas of what the problem could be?
TimeController
import WatchKit
import Foundation
import UserNotifications
class TimerController: WKInterfaceController {
#IBOutlet weak var timerOutlet: WKInterfaceTimer! //
#IBOutlet weak var simple_timer_label: WKInterfaceLabel!
var myTimer : Timer?
var duration : TimeInterval = 1 //arbitrary number. 1 seconds
var isPaused = false //flag to determine if it is paused or not
var elapsedTime : TimeInterval = 0.0 //time that has passed between
var number_as_a_timer:Int = 0
var startTime = NSDate()
var dim_date = Date()
var current_minute: Int = 0
var current_hour: Int = 0
var curent_second: Int = 0
var seperate_is_paused_bool: Bool = false
override func awake(withContext context: Any?) {
super.awake(withContext: context)
start_timer()
}
func timeString(time:TimeInterval) -> String {
let hours: Int = Int(time) / 3600
let minutes: Int = Int(time) / 60 % 60
let seconds: Int = Int(time) % 60
let com = NSDateComponents()
com.minute = minutes
com.second = seconds
com.hour = hours
dim_date = NSCalendar.current.date(from: com as
DateComponents)!
self.timerOutlet.setDate(dim_date)
self.timerOutlet.start()
return String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
func start_timer() {
myTimer = Timer.scheduledTimer(timeInterval: duration, target:
self,selector: #selector(timerDone), userInfo: nil, repeats:
true)
}
#objc private func timerDone(){
//timer done counting down
if !isPaused {
number_as_a_timer += 1
let output:String = self.timeString(time:
TimeInterval(number_as_a_timer))
self.simple_timer_label.setText(output)
print(output)
}
}
override func willActivate() {
super.willActivate()
NotificationCenter.default.addObserver(self, selector:
#selector(stop_timer(notification:)), name: .stopTimer, object:
nil)
}
#objc func stop_timer(notification:NSNotification) {
// Timer is paused. so unpause it and resume countdown
if isPaused {
myTimer = Timer.scheduledTimer(timeInterval: 1,
target:self, selector: #selector(timerDone), userInfo: nil,
repeats: true)
self.isPaused = false
print("timer paused: resumming1")
} else {
isPaused = true
print("stoping timer")
//get how much time has passed before they paused it
let paused = NSDate()
elapsedTime += paused.timeIntervalSince(startTime as Date)
//stop watchkit timer on the screen
timerOutlet.stop()
//stop the ticking of the internal timer
myTimer!.invalidate()
}
}
}
extension Notification.Name {
static let stopTimer = Notification.Name("stopTimer")
}
SwipeController
import WatchKit
import Foundation
import UserNotifications
class SwipeController: WKInterfaceController {
//#IBOutlet weak var myTimer: WKInterfaceTimer!
var timer = TimerController()
var status: Bool = false
override func awake(withContext context: Any?) {
super.awake(withContext: context)
}
#IBAction func PauseButton() {
if timer.myTimer == nil {
print("timer is nil or invalidated")
print("Y: \(timer.isPaused)")
let userInfo = ["stop": true] as [String: Bool] // you
could also transfer data
NotificationCenter.default.post(name: .stopTimer, object:
nil, userInfo: userInfo)
} else {
print("empty block")
}
}
}
it looks like you aren't ever actually checking for you isPaused boolean to be true or false in your if statement when checking if your timer is paused.
if isPaused { <-----------
myTimer = Timer.scheduledTimer(timeInterval: 1,
target:self, selector: #selector(timerDone), userInfo: nil,
repeats: true)
self.isPaused = false
print("timer paused: resumming1")
I have a Progress view bar that I would like to use to indicate time. This is my first project in Swift, and I am unsure how to go about this. So any help/ advise would be appreciated ...
(Using Xcode 7.2 and Swift 2.0)
Below Is my view controller. When 'btnPlaySession' is triggered, the content on the view controller is changed every 20 seconds. While the timer is counting to 20, id like to indicate this with the progress bar (so the progress bar resets, each time the content changes).
class CreatedSessionViewController: UIViewController {
var createdSession: [YogaPose]!
var poseDuration: Double = 20.00
var timer = NSTimer!()
var currentPoseIndex = 1
//Outlets:
#IBOutlet var poseProgressView: UIProgressView!
#IBOutlet var lblPoseCount: UILabel!
#IBOutlet var lblPoseName: UILabel!
#IBOutlet var imgPose: UIImageView!
#IBOutlet var tvDescription: UITextView!
// Do any additional setup after loading the view:
override func viewDidLoad() {
super.viewDidLoad()
displayFirstPoseInArray()
}
func displayFirstPoseInArray(){
lblPoseCount.text = (String(currentPoseIndex) + "/" + String(createdSession.count))
lblPoseName.text = createdSession[0].title
imgPose.image = UIImage(named: String(format: "%d.jpg", (createdSession[0].id)!))
tvDescription.text = createdSession[0].desc
}
#IBAction func btnPlaySession(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(poseDuration, target: self, selector: "getNextPoseData", userInfo: nil, repeats: true)
}
func getNextPoseData(){
if (currentPoseIndex < createdSession.count){
setProgressBar()
lblPoseCount.text = (String(currentPoseIndex + 1) + "/" + String(createdSession.count))
lblPoseName.text = createdSession[currentPoseIndex].title
imgPose.image = UIImage(named: String(format: "%d.jpg",(createdSession[currentPoseIndex].id)!))
tvDescription.text = createdSession[currentPoseIndex].desc
currentPoseIndex += 1
print(currentPoseIndex)
}
}
func setProgressBar(){
}
}
OK - so if you want the progress bar to update every second, then you need a timer that fires every second - but which does it 20 times, and calls setProgressBar as selector instead of getNextPoseData
within setProgressBar, you need to increment a class-level attribute, indexProgressBar perhaps, and simply set the progress bar attribute progress to 1.0 / indexProgressBar
if indexProgressBar == 20, then call getNextPoseData, and reset your progress bar
and here's a simplified version of how you might do that
class ViewController: UIViewController
{
#IBOutlet weak var progressBar: UIProgressView!
var timer = NSTimer!()
var poseDuration = 20
var indexProgressBar = 0
var currentPoseIndex = 0
override func viewDidLoad()
{
super.viewDidLoad()
// initialise the display
progressBar.progress = 0.0
}
#IBAction func cmdGo(sender: AnyObject)
{
// display the first pose
getNextPoseData()
// start the timer
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "setProgressBar", userInfo: nil, repeats: true)
}
func getNextPoseData()
{
// do next pose stuff
currentPoseIndex += 1
print(currentPoseIndex)
}
func setProgressBar()
{
if indexProgressBar == poseDuration
{
getNextPoseData()
// reset the progress counter
indexProgressBar = 0
}
// update the display
// use poseDuration - 1 so that you display 20 steps of the the progress bar, from 0...19
progressBar.progress = Float(indexProgressBar) / Float(poseDuration - 1)
// increment the counter
indexProgressBar += 1
}
}
For Continues loader
func setProgress() {
time += 0.1
ProgressBar.setProgress(time / 3, animated: true)
if time >= 3 {
self.time = 0.01
ProgressBar.progress = 0
let color = self.downloadProgressBar.progressTintColor
self.downloadProgressBar.progressTintColor = self.downloadProgressBar.trackTintColor
self.downloadProgressBar.trackTintColor = color
}
I'm a complete newbie to Swift. I have created a running pace calculator as experiment in Playground and it works perfectly, but I struggle to figure out how to connect it to a UI.
Things in particular I struggle with currently:
Casting a text string to an Int label
Does the function that does the calculation go in the IBAction?
Playground Code
import UIKit
func PaceCalculator (minutes:Double, seconds:Double, distance:Double) -> Double{
return ((minutes*60) + seconds) / distance
}
var paceInSeconds = PaceCalculator(28, 26, 10.1)
var paceInMinutes = paceInSeconds / 60
var roundedMinutes = Double(floor(paceInMinutes))
var decimalSeconds = paceInMinutes - roundedMinutes
var intPace = Int(floor(roundedMinutes))
var seconds = Int(floor(decimalSeconds * 60))
println("Your average pace is \(intPace):\(seconds)/km")
Incomplete Swift Code
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func calculatePaceButton(sender: UIButton) {
}
#IBOutlet weak var minutesTextField: UITextField!
#IBOutlet weak var distanceTextField: UITextField!
#IBOutlet weak var paceLabel: UILabel!
}
Storyboard
Disclaimer: I have no programming experience either. So be gentle on the jargon and explanation.)
Hope this will work for you :
You didn't added secondsTextField as IBOutlet please check that too.
class ViewController: UIViewController {
#IBOutlet weak var minutesTextField: UITextField!
#IBOutlet weak var distanceTextField: UITextField!
#IBOutlet weak var paceLabel: UILabel!
#IBOutlet weak var secondsTextField: UITextField!
#IBAction func calculatePaceButton(sender: UIButton) {
var paceInSeconds = PaceCalculator((minutesTextField.text as NSString).doubleValue, seconds: (secondsTextField.text as NSString).doubleValue, distance: (distanceTextField.text as NSString).doubleValue)
var paceInMinutes = paceInSeconds / 60.0
var roundedMinutes = Double(floor(paceInMinutes))
var decimalSeconds = paceInMinutes - roundedMinutes
var intPace = Int(floor(roundedMinutes))
var seconds = Int(floor(decimalSeconds * 60))
paceLabel.text = "\(intPace)"
}
func PaceCalculator (minutes:Double, seconds:Double, distance:Double) -> Double{
return ((minutes*60) + seconds) / distance
}
}
Here I would like to share my solution to calculate pace. The calculation is based on inputs of times and locations, which are more generic and useful.
typealias ShortFullTupleStrings = (short: String, full: String)
class PaceCalculator {
private static func relatedTimeString(
for value: TimeInterval)
-> ShortFullTupleStrings?
{
let fm = DateComponentsFormatter()
switch abs(value) {
case 0 ..< 24*3600: // within one day
fm.allowedUnits = [.year, .day, .hour, .minute, .second]
case 24*3600 ..< 24*3600*10: // within 1-10 days
fm.allowedUnits = [.year, .day, .hour, .minute]
case 24*3600*10 ..< 24*3600*365: // within 10-365 days
fm.allowedUnits = [.year, .day, .hour]
default: // within 365-1000 days
fm.allowedUnits = [.year, .day]
}
fm.unitsStyle = .short
let short = fm.string(from: value)
fm.unitsStyle = .full
let full = fm.string(from: value)
if let short = short, let full = full {
return (short, full)
} else {
return nil
}
}
static var isMetric: Bool {
let locale = NSLocale.current
let metricSystem = locale.usesMetricSystem
return metricSystem
}
static func paceFrom( _
dt1: Date, to dt2: Date,
distanceFrom loc1: CLLocation, to loc2: CLLocation) ->
ShortFullTupleStrings?
{
let timeInterval = dt2.timeIntervalSince(dt1)
let dist = loc2.distance(from: loc1)
let pace: ShortFullTupleStrings?
if !dist.isZero {
let paceV: TimeInterval
if isMetric {
paceV = timeInterval / (dist / 1000.0)
} else {
paceV = timeInterval / (dist / 1609.344)
}
pace = relatedTimeString(for: paceV)
} else {
pace = nil
}
return pace
}
}
relatedTimeString is a helper func to get a time string in the format of short form of # yrs, # days, # hrs, # min, # sec, and full form of # years, ..., depending on none zero values. For example, 6 min, 5 sec in short form, or 6 minutes, 5 seconds in full form.
In this way, the func is more generic and supports localization and accessibilities.
My question is based on a time interval
I'm getting the hour and minutes (NOW) and I want to create a timer for 75 minutes since that moment but I want the timer to continue working even if i close the app even on multitasking
So i was thinking if I save the time as a NSUserDefault value and every time I open the app It reads the stored value and recalculates
This is my actual code
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(30, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
//here I should update the GUI with the remaining minutes and do a NSUserDefault check if the 75 minutes have already
}
#IBAction func save_time(sender: AnyObject) {
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute, fromDate: date)
var hour = components.hour
var minutes = components.minute + 75
if (minutes>59){
hour += 1
minutes -= 60
}
defs.setInteger(hour, forKey: "u_hora")
defs.setInteger(minutes, forKey: "u_minutos")
}
You can just store them as TimeInterval using NSUserDefaults and you should create a time interval extension to format your time string as desired:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var strTimer: UILabel!
var tasksManager = NSTimer()
var endTime:NSTimeInterval = 0
var now: NSTimeInterval {
return NSDate().timeIntervalSinceReferenceDate
}
func updateTime(){
strTimer.text = ( endTime - now ).time
}
override func viewDidLoad() {
super.viewDidLoad()
// NSUserDefaults().removeObjectForKey("endTime")
// loads endTime if it exists otherwise assign 0 value
endTime = NSUserDefaults().valueForKey("endTime") as? NSTimeInterval ?? 0
// restart timer if endTime exists
if endTime > 0 {
tasksManager = NSTimer.scheduledTimerWithTimeInterval(1/15, target: self, selector: "updateTime", userInfo: nil, repeats:true)
} else {
strTimer.text = "0:01:00.00"
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func startTimer (sender: AnyObject) {
// sets endTime new timeinterval to a future point in time
// so we need to add n seconds from now
endTime = now + 60.0 // now + n seconds
// saves it using NSUserDefaults
NSUserDefaults().setValue(endTime, forKey: "endTime")
// if the timer doest exists we create one
if !tasksManager.valid {
tasksManager = NSTimer.scheduledTimerWithTimeInterval(1/30, target: self, selector: "updateTime", userInfo: nil, repeats:true)
// if the timer exists we invalidate it
} else {
tasksManager.invalidate()
endTime = 0
strTimer.text = "0:01:00.00"
NSUserDefaults().removeObjectForKey("endTime")
}
}
}
extension NSTimeInterval {
var time: String {
return String(format: "%d:%02d:%02d.%02d", Int(self/3600), Int(self/60%60), Int(self%60), Int(self*100%100))
}
}
You can save the current date and time in NSUserDefaults like this.
NSUserDefaults.standardUserDefaults().setObject(NSDate(), forKey: "dateKey")
You can calculate the number of minutes elapsed between saved time and current time in this way.
if let savedDate = NSUserDefaults.standardUserDefaults().objectForKey("dateKey") as? NSDate
{
let currentDate = NSDate()
let distanceBetweenDates = currentDate.timeIntervalSinceDate(savedDate)
let secondsInAnMinute = 60.0;
let minutesElapsed = distanceBetweenDates / secondsInAnMinute;
println(minutesElapsed)
println(distanceBetweenDates)
}