How to reset NSTimer? swift code - ios

So I'm creating an app/game where you tap a button corresponding to a picture before the timer runs out and you lose. You get 1 second to tap the button, and if you choose the right button then the timer resets and a new picture comes up. I'm having trouble reseting the timer. It fires after one second even after I attempt to reset it. Here's the code:
loadPicture() runs off viewDidLoad()
func loadPicture() {
//check if repeat picture
secondInt = randomInt
randomInt = Int(arc4random_uniform(24))
if secondInt != randomInt {
pictureName = String(self.PicList[randomInt])
image = UIImage(named: pictureName)
self.picture.image = image
timer.invalidate()
resetTimer()
}
else{
loadPicture()
}
}
and here's the resetTimer() method:
func resetTimer(){
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("gameOverTimer"), userInfo: nil, repeats: false)
}
I think it may have something to do with NSRunloops? I'm not sure. I don't even know what a NSRunloop is to be honest.

So I finally figured it out...
I had to make a separate function to start the timer by initializing it.
And in the resetTimer() function I added the timer.invalidate() line. So my code looks like this:
func loadPicture() {
//check if repeat picture
secondInt = randomInt
randomInt = Int(arc4random_uniform(24))
if secondInt != randomInt {
pictureName = String(self.PicList[randomInt])
image = UIImage(named: pictureName)
self.picture.image = image
resetTimer()
}
else{
loadPicture()
}
}
func startTimer(){
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("gameOverTimer"), userInfo: "timer", repeats: true)
}
func resetTimer(){
timer.invalidate()
startTimer()
}
EDIT
With the new selector syntax it looks like this:
func startTimer(){
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(NameOfClass.startTimer), userInfo: "timer", repeats: true)
}

Related

How do I cancel a delayed action if the same button that triggered it is clicked before the delayed time is executed?

class viewcontroller1: UIViewController {
I have these two images. When the next button is clicked, image1 changes to image2 after 10 seconds. However, I have no idea how to cancel/reset the delayed action if the next button is clicked again before the 10 seconds has expired. This is the code I have so far...
var image1: UIImageView!
var image2: UIImageView!
func delay(_ delay:Double, closure:#escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
#IBAction func nextbutton(_ sender: Any) {
image1.image = UIImage(named: "image1")
delay(10) { self.image2.image = UIImage(named: "image2")
}
}
}
I would really appreciate any help. Thanks
anyncAfter can not be canceled
As per comment you have to use Timer following is example
Create global variable
var timer:Timer?
and On action
#IBAction func btnImageChangedTapped(_ sender: UIButton) {
if (self.timer != nil) {
self.timer?.invalidate()
self.timer = nil;
}
timer = Timer.scheduledTimer(timeInterval: yourTime, target: self, selector: #selector(changeImage:), userInfo: sender, repeats: false)
}
u can try this one
var timer : Timer?
func startTimer(){
timer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(self.YourImageChangeAction), userInfo: nil, repeats: false)
}
func resetTimer(){
timer?.invalidate()
startTimer()
}
add func resetTimer to your UiButton sender.
You could use a click counter in your VC and test it upon execution of the delayed code.
for example:
var clickCount = 0
#IBAction func nextbutton(_ sender: Any)
{
clickCount += 1
let wantsClickCount = clickCount
image1.image = UIImage(named: "image1")
delay(10)
{
guard self.clickCount == wantsClickCount
else { return }
self.image2.image = UIImage(named: "image2")
}
}
if the button was clicked again before the closure executes, the clickCount will no longer match and the timed code will do nothing.
This would even work with very fast clicking. Note that it will push off the image change by 10 seconds each time so the image change will always happen 10 seconds after the last click.

NSTimer invalidate not work

I can not stop the timer NSTimer created to perform secondary operations.
I have read and tried all sorts of possible Web guides but none have worked and the timer continues to run even when the Controller is destroyed. Below is the code in question:
/* Copy banner in a temp array and change order to random value */
private func getBanners(){
let realm = try! Realm()
let banners = self.synchronizer.getAllBanners()?.sorted("ordine")
if let banners = banners {
for banner in banners {
let random = Double.random(0.0, 1.0)
let currentOrderValue = banner.ordine
self.banners.append(banner)
do {
try realm.write({
self.banners.last!.ordine = currentOrderValue + random
})
} catch let error as NSError {
print(error)
}
}
}
}
/*Update index of banner to show */
func updateIndex(timer : NSTimer) {
if self.index+1 < self.banners.count {
for banner in self.banners{
print(banner.name + " \(banner.ordine)")
}
self.index+=1
} else {
self.index = 0
}
self.setImageBanner()
}
/* Set image of banner to show*/
private func setImageBanner() {
if self.banners.count > 0 {
self.bannerImage.hidden = false
let banner = self.banners[self.index]
let file = banner.images[0]
self.synchronizer.loadImageURL(file.link, imageView: self.bannerImage)
} else {
self.bannerImage.hidden = true
}
}
/* Start Timer */
func startTimerForBanners() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.getBanners()
})
self.timer = NSTimer(timeInterval: 5, target: self, selector: #selector(self.updateIndex(_:)), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
}
/* Open link on banner click */
func openLink(sender : UITapGestureRecognizer){
if self.index >= 0 && self.index < self.banners.count {
let banner = self.banners[self.index]
print(banner.name)
self.synchronizer.openLink(banner.url)
}
}
func trialLesson(sender : UITapGestureRecognizer){
performSegueWithIdentifier("trial_lesson_segue", sender: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if let timer = self.timer {
timer.invalidate()
}
}
you need to invalidate the timer before starting a new timer. Currently I believe you are calling "startTimerForBanners" many times may be, so many timer thread has been initiated. One approach is to invlidate all timer one by one or just invalidate before starting new one like this
/* Start Timer */
func startTimerForBanners() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.getBanners()
})
**self.timer.invalidate()**
self.timer = NSTimer(timeInterval: 5, target: self, selector: #selector(self.updateIndex(_:)), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
}
Are you sure that your controller is really deallocated?
There is one thing you should be always aware about NSTimer: it retains it's target.
I would suggest to:
Start your timer in viewWillAppear
Invalidate and set timer to nil in viewDidDisappear
Also try replacing your start timer code with this (Swift 3):
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.updateIndex(_:)), userInfo: nil, repeats: true)

Swift 2 - Timed Actions one second apart?

I'm trying to get output like so:
1 (then a one second delay)
Hello
2 (then a one second delay)
Hello
3 (then a one second delay)
Hello
But instead I get
1
2
3 (then a one second delay)
Hello
Hello
Hello
Here's my for loop invoking the NSTimer
var timer = NSTimer()
for i in 1...3 {
print("\(i)");
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(MainVPScreenViewController.printTest), userInfo: nil, repeats: false)
}
And here's the selector method:
func printTest() {
print("Hello")
}
Thanks in advance for any help you can provide
Try this solution without NSTimer:
var i = 1
func printHello() {
print(i)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
print("Hello")
i +=1
if i <= 3 {
printHello()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
printHello()
}
I need 2 NSTimers to do this, this is my approach
class ViewController: UIViewController {
var i = 1
override func viewDidLoad() {
super.viewDidLoad()
beginPrinting()
}
func beginPrinting() {
var timer2 = NSTimer()
if(i <= 100)
{
timer2 = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.printWithDelay), userInfo: nil, repeats: false)
}
}
func printWithDelay()
{
var timer = NSTimer()
print("\(i)");
i += 1
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.printTest), userInfo: nil, repeats: false)
}
func printTest() {
print("Hello")
beginPrinting()
}
}
Hope this helps you
Use timer with repeat to true. So in your view controller would be like this
var timer = NSTimer()
var counter = 0
var max = 10
let delay = 1 // in second
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self,
selector: #selector(self.printTest), userInfo: nil, repeats: true)
}
func printTest() {
counter += 1
print(counter)
print(hello)
if counter == maxNumber {
timer.invalidate()
}
}
This does it with repeat false, and is set up to be in a playground:
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
#objc class Foo: NSObject {
static var timer = NSTimer()
var i:Int
override init() {
Foo.timer = NSTimer()
i = 1
}
func schedule() {
print("\n\(i)");
i += 1
Foo.timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: #selector(printTest),
userInfo: nil,
repeats: false)
}
#objc func printTest() {
print("Hello")
if i < 5 {
schedule()
}
}
}
let bar = Foo()
bar.schedule()

How to check if user doesn't write anything in 4 seconds?

I have the next function:
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
if textView.text.characters.count == 0 {
print("stopped")
} else {
print("typing")
}
}
So, I want the next:
For example, user is typing something and stopped in the middle of text. I want to check, if user wrote something and stopped in the middle of typing for 4 seconds to run the function stopped().
I did this:
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
if textView.text.characters.count == 0 {
print("stopped")
} else {
print("typing")
let timer = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: "stopped", userInfo: nil, repeats: false)
}
}
func stopped() {
print("stopped typing")
}
but it runs my NSTimer every time. That's not what I want.
How can I run it just one time? For example to check if user didn't write anything in 4 seconds to run stopped(). Just one time.
You need to stop the previous timer with invalidate
var timer
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
if textView.text.characters.count == 0 {
print("stopped")
} else {
timer.invalidate()
print("typing")
timer = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: "stopped", userInfo: nil, repeats: false)
}
}
You'll have to keep a reference on your timer and invalidate it whenever textViewDidChange is called, something like that :
var textViewTimer : NSTimer?
override func textViewDidChange(textView: UITextView) {
super.textViewDidChange(textView)
textViewTimer.invalidate()
if textView.text.characters.count == 0 {
print("stopped")
} else {
print("typing")
textViewTimer = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: "stopped", userInfo: nil, repeats: false)
}
}
func stopped() {
print("stopped typing")
}
Here is a solution for Swift 5
var textViewTimer: Timer?
override func textViewDidChange(textView: UITextView) {
textViewTimer.invalidate()
print("typing")
textViewTimer = Timer.scheduledTimer(timeInterval: 4.0, target: self, selector: #selector(typingStopped), userInfo: nil, repeats: false)
}
}
#objc func typingStopped() {
print("stopped")
}

How can I disable a button for a few seconds after it is tapped

The following is the code for an IBAction I would like to disable for 5 seconds after it is clicked.
#IBAction func postPressed(sender: AnyObject) {
//var disableMyButton = sender as? UIButton
//disableMyButton!.enabled = false
//NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector:"enableButton", userInfo: nil, repeats: false)
if(currLocation != nil){
let testObject = PFObject(className: "Hey")
testObject["text"] = self.postView.text
testObject["count"] = 0
testObject["replies"] = 0
testObject["location"] = PFGeoPoint(latitude: currLocation!.latitude , longitude: currLocation!.longitude)
testObject["comments"] = []
testObject["user"] = PFUser.currentUser()
testObject.saveInBackgroundWithBlock({ (success, error) -> Void in
if success {
self.dismissViewControllerAnimated(true , completion: nil)
if let completion = self.completion {
completion(self)
}
}
})
} else {
alert()
}
}
The commented out code is my attempt at disabling the button for 5 seconds, but this code crashes, and I believe it crashed because there is no reference to the enable button.
Typically I would do something like
func enableButton() {
self.button.enabled = true
}
But this won't work since it's an IBAction and theres no reference to the button besides there. Maybe there is a way to do so, but I'm at a loss.
Thanks in advance for some help.
Try this,
while tap the action set button disable.
set some interval to enable
your button by using delay function.
Ex:
let tapGesture = UITapGestureRecognizer(target: self, action: "tapaction")
tapGesture.numberOfTapsRequired = 1
Your_Button_name.addGestureRecognizer(tapGesture)
#IBAction func tapaction()
{
Your_Button_name.disable=true
//Delay function to enable your button
NSTimer.scheduledTimerWithTimeInterval(Your_time_value_delay, target: self, selector: Selector("enablefunc"), userInfo: nil, repeats: false)
}
func enablefunc()
{
Your_Button_name.disable=false
}
SWIFT 5.2
within UIButton Action place this code:
yourButtonOutlet.isEnabled = false
//Delay function to enable your button for 3 seconds
Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.enablefunc), userInfo: nil, repeats: false)
Then outside of the UIButton func put:
#objc func enablefunc()
{
yourButtonOutlet.isEnabled = true
}
var disableMyButton = sender as? UIButton
disableMyButton!.enabled = false
testObject.saveInBackgroundWithBlock({ (success, error) -> Void in
if success {
self.dismissViewControllerAnimated(true , completion: nil)
if let completion = self.completion {
completion(self)
}
} else {
disableMyButton!.enabled = true
}
}
Also, depending on what completion(self) does, you may want to do it before you dismiss the view controller. I dunno.
you can use the dispatch framework to add a delay. Use the main thread.
disableMyButton!.enabled = false
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(5 * Double(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue()) {
disableMyButton!.enabled = true
}

Resources