15

I'm trying to pass a UIButton with a NSTimer's userinfo. I've read every post on stackoverflow on NSTimers. I'm getting very close but can't quite get there. This post has helped

Swift NSTimer retrieving userInfo as CGPoint

func timeToRun(ButonToEnable:UIButton) {
    var  tempButton = ButonToEnable
    timer = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("setRotateToFalse"), userInfo: ["theButton" :tempButton], repeats: false)    
}

the function the timer runs

func setRotateToFalse() {
    println(  timer.userInfo )// just see whats happening

    rotate = false
    let userInfo = timer.userInfo as Dictionary<String, AnyObject>
    var tempbutton:UIButton = (userInfo["theButton"] as UIButton)
    tempbutton.enabled = true
    timer.invalidate()    
}
David
  • 3,285
  • 1
  • 37
  • 54
user2164327
  • 283
  • 1
  • 2
  • 13

3 Answers3

32

I realise you've managed to fix this but I thought I would give you a little more information about using NSTimer. The correct way to access the timer object and hence user info is to use it like below. When initialising the timer you can create it like this:

Swift 2.x

NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("setRotateToFalse:"), userInfo: ["theButton" :tempButton], repeats: false)

Swift 3.x<

Timer.scheduledTimer(timeInterval: 1, target: self, selector:#selector(ViewController.setRotateToFalse), userInfo: ["theButton" :tempButton], repeats: false)

Then the callback looks like this:

func setRotateToFalse(timer:NSTimer) {
    rotate = false
    let userInfo = timer.userInfo as Dictionary<String, AnyObject>
    var tempbutton:UIButton = (userInfo["theButton"] as UIButton)
    tempbutton.enabled = true
    timer.invalidate()    
}

Therefore you don't need to keep a reference to the timer and avoid often nasty global variables where possible. You may run into an issue in swift if your class doesn't inherit from NSObject where it says there is no callback defined but this can be easily fixed by adding @objc at the beginning of the function definition.

Community
  • 1
  • 1
Gerard Wilkinson
  • 1,512
  • 14
  • 33
  • No problem. Could you mark an answer when you have a moment. Cheers – Gerard Wilkinson Mar 22 '15 at 17:03
  • 2
    Additionally, you don't absolutely have to pass a dictionary as `userInfo`, you just need an object that conforms to an `AnyObject?`. Personally, I used it to pass a `String` – tfrank377 Feb 24 '16 at 17:56
2

macOS 10.12+ and iOS 10.0+ introduces a block based API of Timer which is a more convenient way

func timeToRun(buttonToEnable: UIButton) {
    timer = Timer.scheduledTimer(withTimeInterval:4, repeats: false) { timer in 
        buttonToEnable.enabled = true
    }    
}

A one shot timer will be invalidated automatically after it fires.


An similar convenient way for a one shot timer is using GCD (DispatchQueue.main.asyncAfter)

func timeToRun(buttonToEnable: UIButton) {
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4)) {
        buttonToEnable.enabled = true
    }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
1

I was just going to post this as I read over it before I posted. I noticed that I had timer.invalidate() before userinfo so that's why it wasn't working. I will post it as it may help somebody else.

func setRotateToFalse(timer:NSTimer) {
    rotate = false
    timer.invalidate()
    let userInfo = timer.userInfo as Dictionary<String, AnyObject>
    var tempbutton:UIButton = (userInfo["theButton"] as UIButton)
    tempbutton.enabled = true
}
David
  • 3,285
  • 1
  • 37
  • 54
user2164327
  • 283
  • 1
  • 2
  • 13