I have a watchkit timer,
However, I have no idea how to perform an action like a notification or segue when the timer ends.
Any ideas?
#IBOutlet var sceneTimer: WKInterfaceTimer!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
sceneTimer.setDate(NSDate(timeIntervalSinceNow :21) as Date)
}
override func willActivate() {
super.willActivate()
sceneTimer.start()
}
override func didDeactivate() {
super.didDeactivate()
}
Yes I am new to this whole thing so its not a ground-breaking code, feel free to correct.
Someone could certainly improve upon this I'm sure. But I built a little watchOS App with a Start Button, a timer and a label. Hook up your WKInterfaceTimer, WKInterfaceLabel and Button as appropriate and this code should work. You can also download the project from GitHub.
var theTimer = Timer()
var backgroundTimer = TimeInterval(15)
#IBOutlet var appleTimer: WKInterfaceTimer!
#IBOutlet var label: WKInterfaceLabel!
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
appleTimer.setDate(Date(timeIntervalSinceNow: 15))
label.setText("")
}
#IBAction func startButton() {
let startTime = Date(timeIntervalSinceNow: 15)
appleTimer.setDate(startTime)
appleTimer.start()
// This will call timerCountDown() once per second until conditions are met.
theTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerCountDown), userInfo: nil, repeats: true)
}
func timerCountDown() {
backgroundTimer -= 1.0
print(backgroundTimer)
if backgroundTimer == 0 {
theTimer.invalidate()
appleTimer.stop()
// You could call an Alert Action here.
label.setText("Timer Done!")
}
}
WKInterfaceTimer is just a label that can be used to display a countdown. It has no associated function that is called by the system once the countdown reaches zero. You need to configure a Timer object with the same target date if you need to know when the countdown reaches 0.
Related
So I have a slider and a startTimer button that calls a nextFood() function via a Timer.scheduledTimer interval based on the slider's value.
What I'm trying to do is even after I pressed the startTimer button, if I move the slider to a different value, the scheduledTimer should adjust to the new value and call the nextFood function according to the new interval without trying to press the startTimer button again.
My code:
#IBOutlet var sliderVal: UISlider!
#IBAction func slider(_ sender: UISlider) {
delayLabel.text = "Delay: " + String(Int(sender.value)) + "s"
}
//start timer according to slider val
var timer = Timer()
#IBAction func startTimer(_ sender: UIButton) {
//print(Int(sliderVal.value))
startButton.isEnabled = false
timer = Timer.scheduledTimer(timeInterval: Double(Int(sliderVal.value)), target: self, selector: #selector(ViewController.nextFood), userInfo: nil, repeats: true)
}
So far my code only works for the value the slider is set when the startTimer button is pressed initially but doesn't adjust when I move the slider. Any help is much appreciated!
Short Answer: You can't change the time interval of the timer once it has started.
What you can do is this: Upon slider action, invalidate the previous timer, create a new timer, and fire it with the new interval.
This is also related to your issue.
You don't need to create two #IBActions. You can create one common #IBAction and connect the button and slider action to that.
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var slider: UISlider!
var timer: Timer?
#IBAction func updateTimer(_ sender: Any) {
label.text = "Delay: " + String(Int(slider.value)) + "s"
startButton.isEnabled = false
timer?.invalidate()
timer = nil
timer = Timer.scheduledTimer(timeInterval: Double(Int(slider.value)), target: self, selector: #selector(ViewController.nextFood), userInfo: nil, repeats: true)
}
#objc func nextFood() {
///
}
}
Note: Before initialising new Timer instance, invalidate the previous instance. Else multiple timer instances will be calling the nextFood method
You can do that with a DispatchSourceTimer which can be restarted without recreating the timer instance
var timer : DispatchSourceTimer?
#IBOutlet var sliderVal: UISlider!
#IBAction func slider(_ sender: UISlider) {
let delay = Int(sender.value)
delayLabel.text = "Delay: \(delay) s"
startTimer(with: delay)
}
//start timer according to slider val
#IBAction func startTimer(_ sender: UIButton) {
//print(Int(sliderVal.value))
startButton.isEnabled = false
startTimer(with: Int(sliderVal!.value))
}
func startTimer(with delay: Int) {
let interval : DispatchTime = .now() + .seconds(delay)
if timer == nil {
timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
timer!.schedule(deadline:interval, repeating: TimeInterval(delay))
timer!.setEventHandler {
DispatchQueue.main.async {
self.nextFood()
}
}
timer!.resume()
} else {
timer!.schedule(deadline:interval, repeating: TimeInterval(delay))
}
}
I'm trying to keep accurate counting and display of a countdown timer between switching view controllers. In order to do so, I added NSNotifications as mentioned in this question: Timer Label not updated after switching views (swift)
The problem is that the timer will be decremented twice after switching back from the other view controller.
This issue seems to be unrelated to the notifications, as the same problem occurs without them, it only becomes apparent after restarting the timer as it isn't updated automatically when reaching the view controller.
I really don't find the cause of this, any help much appreciated!
I've set up this sample code. There's another view controller added to the original one in Main.storyboard, there is one switch and one label displaying the timer added to it. The original view controller only contains one bar button item to trigger the segue to the second view controller.
import Foundation
final class DataModel: NSObject {
static let shared = DataModel()
var isSleepTimerOn = false
var timerTime: NSTimeInterval = 100.0
}
// The second view controller.
import UIKit
class TimerViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var timerSwitch: UISwitch!
var timer: NSTimer?
override func viewDidLoad() {
super.viewDidLoad()
timerSwitch.on = DataModel.shared.isSleepTimerOn
timerLabel.text = String(DataModel.shared.timerTime)
let selector = #selector(setTimerLabel), name = "setTimerLabel"
NSNotificationCenter.defaultCenter().addObserver(self, selector: selector, name: name, object: nil)
}
#IBAction func switchToggled(sender: AnyObject) {
DataModel.shared.isSleepTimerOn = timerSwitch.on
switch timerSwitch.on {
case true:
startTimer()
case false:
stopTimer()
}
}
func startTimer() {
let selector = #selector(decrementTimer)
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: selector, userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer!, forMode: NSRunLoopCommonModes)
}
func decrementTimer() {
DataModel.shared.timerTime -= 1
NSNotificationCenter.defaultCenter().postNotificationName("setTimerLabel", object: nil)
setTimerLabel()
}
func setTimerLabel() {
timerLabel.text = String(DataModel.shared.timerTime)
}
func stopTimer() {
timer?.invalidate()
timer = nil
DataModel.shared.timerTime = 100.0
timerLabel.text = String(DataModel.shared.timerTime)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "setTimerLabel", object: nil)
}
}
EDIT: Solution: move the timer from the view controller class to the DataModel singleton, so there would be only one timer.
Ok, new morning, fresh brain, had another look at the code and the problem was clear: I was initiating a new timer every time I switched to the view controller, so when stopping the timer, it wouldn't stop the previous one. Since there should be only one timer I moved it from the view controller class to the DataModel, which is a singleton.
Let's say I have a class Timer that works as a timer.
I want every other class that can act like a timer to have an attribute of type Timer.
The problem is: the Timer class uses a NSTimer attribute and I don't know how to associate functions from outside Timer in it.
For example: Class A has attribute B, which is Timer. How can scheduledTimerWithTimerInterval call some function of A?
Here is timer:
import UIKit
class Timer{
var interval: NSTimeInterval!
var counting: Bool = false
var numberOfTouches: Int = 0
private var timer: NSTimer = NSTimer()
init(interval: CGFloat){
self.interval = NSTimeInterval(interval)
self.counting = false
}
func Start(function: String) {
if(self.counting){
self.numberOfTouches += 1
self.timer.invalidate()
self.timer = NSTimer.scheduledTimerWithTimeInterval(self.interval, target: self, selector: Selector(function), userInfo: nil, repeats: false)
}
else{
self.counting = true
self.numberOfTouches = 1
}
}
func Stop(){
if(!self.counting){
self.counting = false
}
}
}
here is the ViewController:
import UIKit
class ViewController: UIViewController {
var timer: Timer = Timer(interval: 1.0)
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Trigger(sender: AnyObject) {
//yes, this doesn't call t() from the ViewController
timer.Start("self.t")
}
func t(){
label.text = String(timer.numberOfTouches)
}
#IBOutlet weak var label: UILabel!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Finally, what this timer tries to do is:
when I press the button, it starts counting and set the number of touches to 1;
if I press it again, less than 1 second after the touch before, it restart the 1 second timer and increases the touch by 1;
after 1 second without any touch, it should call t() from the ViewController to set the label to the last number of touches.
What approach should I use to fix this?
Thank you!
PS: in the beginning, it was Selector("Stop")
It's not "self.t", you supply the object as the target. The selector is something like "ViewController.t" or "t". You need to change Start to take a target object as well.
Move to Xcode 7.3.1 and use the new selector syntax if you can -- it will be type-checked.
This is my first post so I hope this is a valid question. I've searched the forums for an answer to this with no luck. Below is my code for a stopwatch app. Problem I am having is when the play button is clicked multiple times it is ticking multiple seconds at a time. How do I safely stop this from happening?
ViewController
import UIKit
class ViewController: UIViewController {
// MARK: Properties
#IBOutlet weak var timerLabel: UILabel!
var timer = NSTimer()
var time = 0
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: Functions
func increaseTimer() {
time++
let formattedTime = String(format:"%02d:%02d", (time/60), time%60)
timerLabel.text = "\(formattedTime)"
}
// MARK: Actions
#IBAction func btnPlay(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self,
selector: Selector("increaseTimer"), userInfo: nil, repeats: true)
}
#IBAction func btnStop(sender: AnyObject) {
timer.invalidate()
}
#IBAction func btnReset(sender: AnyObject) {
timer.invalidate()
time = 0
timerLabel.text = "00:00"
}
}
EDIT: SOVED http://imgur.com/mqw1Xnp
Make your button into a start/stop button. Use a boolean instance variable to keep track of whether the timer is running or not. If it is, stop it. If it's not, start it.
Alternately, make the code that starts the timer set button.disabled = true
This is my code.
//
// ViewController.swift
// Stopwatch
//
// Created by Orkun Atasoy on 12.09.15.
// Copyright (c) 2015 Orkun Atasoy. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var timerButton: UIButton!
var timer : NSTimer?
var ms = 0
#IBAction func buttonTapped(sender: AnyObject) {
timerButton.setTitle("Stopp", forState:UIControlState.Normal)
self.timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
self.ms++
timerLabel.text = String(self.ms)enter code here
}
}
The Problem is when i run the build it comes the introscreen with big text "Stopwatch" and the it is like freezed there. But it should come a label with button downside which has a text "start". When i click the button it should start counting and the label should change to "stopp". When i click again the it should stop the timer.
I dont get what the Problem ist. I am a Swift newbie. I would be pleased if you could help me.
Thank you for your attention
EDIT >> the label text is at the beginning "00:00".
Looks like you are messing with IBOutlet connections and I have corrected some of your code and here is working code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var timerButton: UIButton!
var timer : NSTimer?
var ms = 0
override func viewDidLoad() {
timerLabel.text = "00:00"
}
#IBAction func buttonTapped(sender: AnyObject) {
if timerButton.currentTitle == "Stopp" {
timer?.invalidate()
timerButton.setTitle("Start", forState:UIControlState.Normal)
} else {
timerButton.setTitle("Stopp", forState:UIControlState.Normal)
self.timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
}
func update() {
self.ms++
timerLabel.text = String(self.ms)
}
}
And First of all remove all outlets from your storyboard view controller and connect it this way:
And if you want check THIS sample project.
The method you call when the timer fires is incorrect. From the documentation:
The selector should have the following signature: timerFireMethod: (including a colon to indicate that the method takes an argument). The timer passes itself as the argument, thus the method would adopt the following pattern:
- (void)timerFireMethod:(NSTimer *)timer
So your method should be:
func update(timer: NSTimer) {
self.ms++
timerLabel.text = String(self.ms)
}
And you should set up your timer as:
NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "update:", userInfo: nil, repeats: true)
There are a few more things I could mention - such as no need to use self when unambigously using variables, and using a dispatch timer instead of an NSTimer, but this should at least solve your immediate problem.