NSTimer invalidate not work - ios

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)

Related

Vibrate indefinitely in Swift

How can I make an iPhone vibrate indefinitely? I know I can do this:
while true {
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
}
Is there any way to play the vibration for a certain number of seconds, or something with a similar result that is cleaner? Also, is there a way to make the vibration more intense?
I think you should use Timer.
var timer: Timer?
var numberRepeat = 0
var maxNumberRepeat = 20
func startTimer() {
//
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.timerHandler), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
#objc func timerHandler() {
if numberRepeat <= maxNumberRepeat {
numberRepeat += 1
vibrateDevice()
} else {
stopTimer()
}
}
func vibrateDevice() {
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
}

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
}

How to reset NSTimer? swift code

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)
}

Resources