0

I am new to XCode so any help will be appreciated.

On button press, the button should be disabled and should display a random number and trigger a timer. Once the timer is is done then the button is enabled again.

Here is the button code:

@IBAction func MarketButton(_ sender: UIButton) {
    let t = Int(arc4random_uniform(10))
    MarketLabel.text = String (t)
    let timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(MarketViewController.MarketButton), userInfo: nil, repeats: false)
    sender.isEnabled = !(timer.isValid )
boidkan
  • 4,691
  • 5
  • 29
  • 43
sillyJohn
  • 5
  • 4

2 Answers2

1

welcome to Stack Overflow. Let me walk you through what your code is doing at the moment after the button press.

  1. create a random number between 0 and 10
  2. put the number into the label
  3. schedule the timer to start the same function
  4. if the timer is valid, it will disable the button. If the timer is invalid, it will enable the button.

After the timer fires however, it calls the same function. This time around it won't have the same parameter though. In the first run, the button was passing itself on to the function a the first parameter. This time, the timer will be the first argument. This unfortunately happens as the selector API is not type safe. So here is what happens as soon as the timer is triggered:

  1. The function gets called
  2. a random number is generated and written to your label
  3. a new timer is started
  4. The objective C runtime, which is running your iOS app, tries to find and isEnabled property which it won't find on your timer and it will crash.

Therefore it is crucial for your timer to call another function like @boidkan suggested. I'd suggest something like this:

class TimebombViewController {
    @IBOutlet weak var timerLabel: UILabel!
    @IBOutlet weak var startButton: UIButton!

    var timer: Timer?
    @IBAction startButtonPressed(_ sender:UIButton){
        timerLabel.text = Int(arc4random_uniform(10)).description
        timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(timerFired), userInfo: nil, repeats: false)
        refreshButtonState()
    }

    @objc timerFired(_ timer:Timer){
        timer = nil
        refreshButtonState()
    }

    func refreshButtonState(){
        startButton.isEnabled = !(timer?.isValid ?? false)
    }

}
Geru
  • 624
  • 1
  • 7
  • 20
0

You have a few problems here:

What is causing the crash is that MarketButton requires you to pass in a UIButton as a parameters which the timer is not doing when triggered. You can do this by passing in the button through the userInfo parameter of scheduledTimer. Here is a stack overflow post on how to do that.

However, even after you do this things will still not work. You need to make a method to handle when the timer is done.

Something like:

func enableButton() {
    yourButton.isEnabled = true
}

And then put that as the selector instead of the MarketButton method.

like so:

let timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(enableButton), userInfo: nil, repeats: false)

By putting "MarketButton" as the selector of the timer you are causing an infinite loop. When the timer is done it calls the method which then triggers another timer and so on.

Another issue is with these two lines of code:

let timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(MarketViewController.MarketButton), userInfo: nil, repeats: false)
sender.isEnabled = !(timer.isValid )

In this case timer will almost always be valid because you just set it. So !(timer.isValid) will basically always return false. However, if you follow my advice and trigger a different method instead of MarketButton then this will not be a problem.

Also a side note, when naming functions you should not capitalize them so MarketButton should be marketButton.

I suggest trying to find the solution with the information I gave you. If you have any questions let me know and welcome to stack overflow!

boidkan
  • 4,691
  • 5
  • 29
  • 43
  • I think the most important reason this thing is crashing is the fact that the timer is calling a selector, MarketButton: which is requiring a button to be its first and only argument. Any other of your remarks still apply, but it should fail because the timer is not of type UIButton which is why it won't be able to check the isEnabled property on that object. – Geru Sep 21 '18 at 19:55
  • Hmmm would you like to post a solution? Multiple answer are needed :O – boidkan Sep 21 '18 at 19:57
  • 1
    I think you could just add my remark to your answer. Otherwise I'll just be reposting most of your original answer ;-) – Geru Sep 21 '18 at 19:58
  • Thanks you both, @boidkan For your help and salutation, unfortunately, I am still having a little problem with the code.The timer dose not seem not to work and the button dose not disable. – sillyJohn Sep 22 '18 at 13:59
  • var timer: Timer? A IBOutlet weak var marketButtons: UIButton! A IBAction func marketButton(_ sender: UIButton) { marketLabel.text = Int(arc4random_uniform(10)).description timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(enableButton), userInfo: nil, repeats: false) } A objc func enableButton() { timer = nil marketButtons.isEnabled = !(timer?.isValid ?? false) – sillyJohn Sep 22 '18 at 14:04
  • @sillyJohn Sorry was out for the weekend! Were you able to get it to work? – boidkan Sep 24 '18 at 17:04
  • @boidkan I get it But the button reset if i turn back to different view – sillyJohn Sep 26 '18 at 19:48