problems with Timer on Swift - ios

I'm having a problem with my timer on swift. When I press the play button once it works fine and adds one second every second but when I play it twice it adds 2 seconds every one second and so on. This is my code.
var timer = NSTimer()
var time = 0
var minute = 0
#IBOutlet var timerLabel: UILabel!
func increasedTimer()
{
time++
timerLabel.text = "\(minute)" + "m " + "\(time)" + "s"
if time == 59
{
minute++
time = -1
}
}
#IBAction func play(sender: AnyObject)
{
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("increasedTimer"), userInfo: nil, repeats: true)
}
#IBAction func reset(sender: AnyObject)
{
timer.invalidate()
time = 0
minute = 0
timerLabel.text = "0m 0s"
}
#IBAction func pause(sender: AnyObject)
{
timer.invalidate()
}

Every time you tap play you create and start an additional timer.
You have a few options to fix this.
From a user experience point of view, you need to enable/disable your three buttons (Play, Pause, Stop) appropriately. It makes no sense that the user can tap Play a 2nd time while the timer is going. And of course the Pause and Stop buttons shouldn't be enabled until Play has been tapped.
So start by fixing the user interface so the buttons make sense. Once that is done, you won't have the problem in your current code since the user won't be able to tap Play twice without first pausing or stopping.

In the short term, every time you play put another timer.invalidate() before starting a new timer. Definitely better to disable the play button.
button.setBackgroundImage(backgroundImage1, forState: .Normal)
button.setBackgroundImage(backgroundImage2, forState: .Highlighted)
button.setBackgroundImage(backgroundImage2, forState: .Disabled)
Examples of things you can do. You can supply different images for a greyed out play button, a selectable play button. Etc. Then change the forState. More options available then shown.
Or you can just change the text shading in the button with:
button.enabled = true
button.enabled = false

Related

How can i count time on long-pressed button using UILongPressGestureRecognizer

How can I count time on how long the button is pressed using UILongPressGestureRecognizer; I am looking to print the long-Pressed count time in the displayLabel.text
I've tried most possible way.
#IBOutlet weak var buttonPressed: UIButton!
#IBOutlet weak var displayLabel: UILabel!
var buttonPressedCount : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let longPressObj = UILongPressGestureRecognizer(target: self, action: #selector(longPressButton))
longPressObj.minimumPressDuration = 2
longPressObj.numberOfTouchesRequired = 1
self.view.addGestureRecognizer(longPressObj)
// below statement is not a right one but i tried many possiblity including this one.
if longPressObj.minimumPressDuration == TimeInterval(2) {
displayLabel.text = "Longpressed for 2 seconds"
} else if longPressObj.minimumPressDuration == TimeInterval(3) {
displayLabel.text = "3"
} else if longPressObj.minimumPressDuration == TimeInterval(3) {
displayLabel.text = "4"
}
}
#IBAction func longPressButton() {
displayLabel.text = "Button pressed for \(buttonPressedCount)"
}
I want to display time of the long-Pressed NOT BUTTON CLICKED.
enter image description here
Thank you in advance!
EDITS:-
1. I just want to show the Running duration while the user is performed Long-Press. I would really appreciate the real-time count in the
2. Also is it will be helpful to show the total duration after stop pressing.
(https://i.stack.imgur.com/ppr0W.png)
Your target function should include sender, then you can get the state of the UILongPressGestureRecognizer.
Here is the Apple official document.
First save the gesture begin time, then you can use the current time to subtract the begin pressed time to get the duration in state .ended (or/and .cancelled, .failed).
Sample code:
class ViewController: UIViewController {
var touchBeginTime: Double = 0
var touchCountTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
let longPressObj = UILongPressGestureRecognizer(target: self, action: #selector(longPressButton(sender:)))
longPressObj.minimumPressDuration = 2
longPressObj.numberOfTouchesRequired = 1
self.view.addGestureRecognizer(longPressObj)
}
#IBAction func longPressButton(sender: UILongPressGestureRecognizer) {
switch sender.state {
case .began:
touchCountTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (timer) in
print("User pressing \(Date().timeIntervalSince1970 - self.touchBeginTime) sec.")
})
touchBeginTime = Date().timeIntervalSince1970
break;
case .changed:
//print("Long pressed changed \(Date().timeIntervalSince1970 - touchBeginTime)")
break;
case .ended, .cancelled, .failed:
touchCountTimer?.invalidate()
print("User pressed total \(Date().timeIntervalSince1970 - touchBeginTime) sec.")
break;
default:
break;
}
}
}
Since you want to show the running duration while the long press is being performed, you need to use a timer.
Start the timer when the long press reaches the .began state and stop (invalidate) the timer when the long press reaches the .ended or .canceled state.
Your time should repeat every second and update the label based on the difference between the current date and the date the long press began.
Rather than using a long press gesture recognizer, why not attach button actions to the touchDownInside event and the touchUpInside and touchUpOutside events on your button. your touchDownInside code can start your timer, which would update the label. The touchUpInside/touchUpOutside action would stop the timer.

NSTimer to Change Text

Sorry if this is a really basic questions, but I can’t seem to work it out, so I thought I would ask the experts.
I’ve got a timer for my project that counts down and updates a label according to what is stored in an array.
var array : String[]()
var x = 0
#IBAction func playBtnPressed(sender: UIButton)
{
timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: #selector(PlayVC.update), userInfo: nil, repeats: true)
}
func update()
{
if x < array.count {
let item = array[x]
aLbl.text = array.itemTitle
x += 1
}
}
My problem is that the text is only updated after the first countdown and 60 seconds is a long time to wait lol.
I would actually like the first String in my array to appear as soon as the button is tapped.
Is there a way to set the text at the very beginning of the countdown?
Thank you for your help :)
So you want to update a label every minute. And you also want to update it immediately after the button is pressed. Hopefully I didn't misunderstand the question.
It's actually as easy as adding this line before the timer = NSTimer ... line:
update()
Note that your current code can cause two or more timers to be created and run when the button is pressed more than once. You might not want this.
To stop the timer when the button is pressed a second time, do this:
if timer == nil {
timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: #selector(PlayVC.update), userInfo: nil, repeats: true)
} else {
timer.invalidate()
timer = nil
}
To do nothing when the button is pressed a second time jus remove the else part.

Progress for LongPressureGesture on a Button

I got a button with a LongPressureGesture, and i would like to have a small ProgressView on top of this button as visual feedback for the user that the longPressureGesture is recognized.
I'm stuck on how to detect the beginning of the longPressure and the duration of the longPressure to be able to set the setProgress() on my ProgressView.
EDIT: So i inspired myself from the answers, thank you. Here is what i made. Feel free to comment the following code, maybe there is a better solution.
private var lpProgress:Float = 0
private var startTouch: NSTimer!
#IBAction func pauseJogButtonTouchDown(sender: AnyObject) {
self.startTouch = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "updateLPGestureProgressView", userInfo: nil, repeats: true)
}
func updateLPGestureProgressView() {
self.lpProgress += 0.1
self.lpGestureProgressView.setProgress(self.lpProgress, animated: true)
if self.lpProgress >= 1 {
self.startTouch.invalidate()
self.pauseBarButton.hidden = true
self.lpGestureProgressView.setProgress(0.0, animated: false)
self.toolbarHomeMadeView.hidden = false
self.switchToState(.Paused)
}
}
#IBAction func pauseJogButtonTouchUpInside(sender: AnyObject) {
self.lpProgress = 0
self.startTouch.invalidate()
self.lpGestureProgressView.setProgress(0.0, animated: false)
}
You do not need the LongPressureGesture in this case.
Use "Touch Down" IBAction of UIButton to start NSTimer, and "Touch Up Inside" to stop timer and check if the delay was right.
ProgressView you can fill by timer progress.
Set up an NSTimer on touchesBegan.
At the same time start your animation to animate the view.
When touchesEnded is triggered then stop the animation if the NSTimer has not triggered yet and cancel the timer.
When the timer finishes run your desired action.
Long Press isn't really designed for this sort of thing.

How to invalidate an NSTimer that was started multiple times

I made a practice project in Swift to learn how NSTimer works. There is one button to start the timer and one button to invalidate it. It works fine when I tap each button once. However, when I tap the start timer button multiple times, I am no longer able to invalidate it.
Here is my code:
class ViewController: UIViewController {
var counter = 0
var timer = NSTimer()
#IBOutlet weak var label: UILabel!
#IBAction func startTimerButtonTapped(sender: UIButton) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}
#IBAction func cancelTimerButtonTapped(sender: UIButton) {
timer.invalidate()
}
func update() {
++counter
label.text = "\(counter)"
}
}
I have seen these questions but I wasn't able to glean an answer to my question from them (many are old Obj-C pre-ARC days and others are different issues):
NSTimer() - timer.invalidate not working on a simple stopwatch?
Using an NSTimer in Swift
NSTimer doesn't stop
Unable to invalidate (Stop) NSTimer
NSTimer doesn't stop with invalidate
Can't invalidate, stop countdown NSTimer - Objective C
IOS: stop a NSTimer
You can add timer.invalidate() before starting a new timer in startTimerButtonTapped if you want to reset the timer each time the "start" button is tapped:
#IBAction func startTimerButtonTapped(sender: UIButton) {
timer.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}
I was going to update with an explanation but #jcaron already did it in the comment, so I'm just quoting his text, no need to change it:
Every time you tap on the "Start Timer" button, you create a new timer, while leaving the previous one running, but with no reference to it (since you've overwritten timer with the new timer you just created). You need to invalidate the previous one before you create the new one.
I would like to suggest you to set timer to nil when press on cancel button.
And don't forget to set counter =0
When invalidating the Timer.

How can we use long press to delete whole words, for custom keyboards in iOS 8?

As we know, the original keyboard in iOS can delete whole words by holding down the delete button (⌫) for an extended period of time.
So how can we use the same functionality for custom keyboards in Swift, iOS 8?
Note:
I'm currently using proxy.deleteBackward() for deleting letters, and using:
var gesture = UILongPressGestureRecognizer(target: self, action: "longPressed:")
gesture.minimumPressDuration = 1.0
button.addGestureRecognizer(gesture)
when the button is pressed for a greater amount of time.
Thanks!
I'm not sure how you would be able to do it through gesture recognizer.
The original keyboard behaviour is,
When the button is pressed and kept pressed for initial X-time
interval, it keeps deleting backward.
When the button is kept
pressed after the initial X-time interval, it starts deleting words
instead of just characters.
After the button is pressed the first time, you should probably keep calling your delete function and keep noticing if 'X-time-interval' has elapsed. Pseudocode would be
var startTime: NSDate = NSDate()
var timer: NSTimer?
func deleteButtonPressed(deleteButton: UIButton) {
startTime = NSDate()
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("delete"), userInfo: nil, repeats: true)
}
func delete() {
if !deleteButton.highlighted {
timer.invalidate()
timer = nil
return
}
if ((currentNSDate - startTime ) < "X-time-Interval") {
// delete backward
} else {
/* figure out last space character in text and create NSRange
then
mytextView.text deleteCharactersInRange:theRange
set new text */
}
}

Resources