i have got a problem with my Counter application. When starting the counter everything works fine, but the label is showing the initial value 0.0 plus in addition the new time += 0.2. See the result at the foto below. Does anyone have a solution? Thank you!
import UIKit
class CounterViewController: UIViewController {
var time = 0.0
var timer = Timer()
#IBOutlet weak var timerLabel:UILabel!
#IBAction func startCounter(_ sender:UIButton){
timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector:#selector(CounterViewController.startAction), userInfo:nil, repeats: true)
}
#IBAction func pauseCounter(_ sender:UIButton){
timer.invalidate()
}
#IBAction func stopCounter(_ sender:UIButton){
timer.invalidate()
time = 0.0
timerLabel.text = "\(time)"
}
#objc func startAction(){
time += 0.2
timerLabel.text = "\(time)"
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
see label twice
Try this in timer declaration
var timer:Timer?
and set this in viewDidLoad
timerLabel.text = "0.0"
Also check your xib or storyboard If added same label twice
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 was trying to build a simple ios timer app using swift3. I successfully created an app using the following code. It has three buttons, one to start the timer, one to stop the timer which means reset and one to pause the timer. all the buttons are working but when I click on start once again while the timer is running, the timer interval speeds up (means calls the selector function twice within a second). how to solve this problem.
Here is my code
#IBOutlet weak var lbl: UILabel!
var time = 0
var timer = Timer()
#IBOutlet weak var start: UIButton!
#IBOutlet weak var stop: UIButton!
#IBOutlet weak var pause: UIButton!
#IBAction func start(_ sender: AnyObject) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
}
#IBAction func stop(_ sender: AnyObject) {
//timer.invalidate()
time = 0
lbl.text = "0"
}
#IBAction func pause(_ sender: AnyObject) {
timer.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
}
func action() {
time += 1
lbl.text = String(time)
}
If your start method runs while a timer is already active, you create a second timer. Now you have two timers calling the same action, which accounts for your problem. Scheduled timers are retained by their run loops, so even though you don't have a reference to the old timer, it's still there.
At a minimum you need to either invalidate the old timer or else just keep using the one you have. But there are some things that would help make the code better:
Your timer attribute should probably be a Swift optional. Initializing it as Timer() doesn't do anything useful. It would make more sense if timer could be nil when no timer should be running.
You should probably disable your "start" button when a timer is running. It doesn't make sense for it to be active when the timer has already started. You could do this by setting up an IBOutlet for the button and changing the value if the button's isEnabled property.
One Mistake in Source code.
First Stop exist timer then start new timer.
Code for that is below.
#IBAction func start(_ sender: AnyObject) {
timer.invalidate()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
}
And for stop timer use invalidate timer function.
#IBAction func stop(_ sender: AnyObject) {
timer.invalidate()
}
I think what you need to do is invalidate if there is a timer running
#IBAction func start(_ sender: AnyObject) {
if (timer) {
timer.invalidate()
}
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
}
#IBOutlet weak var lbl: UILabel!
var time = 0
var timer : Timer?
var isPause = false
#IBOutlet weak var start: UIButton!
#IBOutlet weak var stop: UIButton!
#IBOutlet weak var pause: UIButton!
#IBAction func start(_ sender: AnyObject) {
timer?.invalidate()
timer = nil
isPause = false
time = 0
lbl.text = "0"
// or call stop(self.stop)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
}
#IBAction func stop(_ sender: AnyObject) {
timer?.invalidate()
timer = nil
isPause = false
time = 0
lbl.text = "0"
}
#IBAction func pause(_ sender: AnyObject) {
timer.invalidate()
isPause = !isPause // second click to resum...
}
override func viewDidLoad() {
super.viewDidLoad()
}
func action() {
if !isPause {
time += 1
lbl.text = String(time)
}
}
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
How do i stop my timer from running? Not like a pause, but a stop.
import UIKit
class LastManStandingViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var timeTextbox: UITextField!
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var stopButton: UIButton!
var myCounter = 0
var myTimer : NSTimer = NSTimer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timeLabel.text = String(myCounter)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func startTimer(){
myTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTimer"), userInfo: nil, repeats: true)
println("func startTimer")
}
func stopTimer(){
myTimer.invalidate()
myCounter = 0
timeLabel.text = String(myCounter)
println("func stopTimer")
}
func updateTimer(){
timeLabel.text = String(myCounter++)
println("func updateTimer")
}
#IBAction func startButton(sender: AnyObject) {
startTimer()
}
#IBAction func stopButton(sender: AnyObject) {
stopTimer()
}
}
I can start the timer, but when i press the Stop button, it reset itself, and starts counting again. It doesn't stop.
Made it work. Something was buggy with my project! Fixed it by removing the button and re-adding them. Looks like i had a duplicate or something.
You don't have to use Selector:
#IBAction func startButton(sender: AnyObject) {
myTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateTimer:", userInfo: nil, repeats: true)
}
Also, the timer passes itself to the selected method, so you can invalidate it inside the method if you need:
func updateTimer(timer: NSTimer) {
timeLabel.text = String(Counter++)
timer.invalidate()
}
Or if the timer is an instance variable:
myTimer.invalidate()
myTimer = nil
It's a good thing to nil the instance variable timer after having invalidated it, it avoids further confusion if you need to create another timer with the same variable. Also, method names and variables should begin with a lowercase letter.
Screenshot to show the timer invalidated and set to nil.
Update for Swift 2.2+
See https://stackoverflow.com/a/36160191/2227743 for the new #selector syntax replacing Selector().
you can use this when some condition is met and you want to stop timer:
Timer.invalidate()
Here is simple example :
func UpdateTimer(){
timeLabel.text = String(Counter++)
if timeLabel.text == String("5") {
Timer.invalidate()
}
}
this will stop timer.
You can modify it as per your need.
As pre Swift 2.2
let printTimer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(printDescription), userInfo: nil, repeats: true)
func printDescription() {
print("Print timer will print every after 2.0 seconds")
}
Print timer will print every after 2.0 seconds
I have a working countdown which you can increase by pushing the add button and thats what the time counts down from (so basically a user can set the countdown)
I would like to display the starting time as 00:00 as it does in my label.
When I click button to increase the countdown it just begins at 1 obviously because at the moment its just an Int
So i had a thought of create a dictionary something like this
var timeDictionary : [Double : Double] = [00 : 00]
I just want to increase to 00:01, 2, 3 when the + button is pressed and start from 00:00. Can anyone help with this if possible please?
this is my full code for the countdown
var timeDictionary : [Double : Double] = [00 : 00]
var timer = NSTimer()
var countdown = 0
func runTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self,
selector:Selector("updateTimer"), userInfo: nil, repeats: true)
}
func updateTimer() {
if countdown > 0 {
countdown--
TimerLabel.text = String(countdown)
} else {
countdown = 0
TimerLabel.text = String(countdown)
}
}
#IBOutlet weak var TimerLabel: UILabel!
#IBAction func IncreaseCountdown(sender: AnyObject) {
countdown++
TimerLabel.text = String(countdown)
}
#IBAction func StartCountdown(sender: AnyObject) {
runTimer()
}
#IBAction func StopCountdown(sender: AnyObject) {
timer.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The simplest approach is just to format the counter as you display it -
#IBOutlet weak var timerLabel: UILabel!
var timer = NSTimer()
var countdown = 0
func runTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector:Selector("updateTimer"), userInfo: nil, repeats: true)
}
func updateTimer() {
if --countdown < 1 {
timer.invalidate()
countdown=0;
}
self.updateTimerLabel();
}
#IBAction func IncreaseCountdown(sender: AnyObject) {
countdown++
self.updateTimerLabel()
}
#IBAction func StartCountdown(sender: AnyObject) {
runTimer()
}
#IBAction func StopCountdown(sender: AnyObject) {
timer.invalidate()
}
func updateTimerLabel() {
TimerLabel.text =NSString(format:"%02d:%02d",seconds/60,seconds%60)
}
}
Note that I also changed TimerLabel to timerLabel as the convention is that variables should start with a lowercase letter