MM1010
MM1010

Reputation: 573

How do I prevent repeated button presses in iOS?

I'd like to stop a button from being pressed for a certain amount of time after initially being pressed successfully.
My first thought is to user a timer and disable the button for a certain amount of time but I'm unsure how to implement this.

Could someone point me in the right direction please?

I'm using swift.

Upvotes: 7

Views: 6733

Answers (6)

Nike Kov
Nike Kov

Reputation: 13726

#Swift 3.0# I think this extension is convenience:

extension UIControl {
    func preventRepeatedPresses(inNext seconds: Double = 1) {
        self.isUserInteractionEnabled = false
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds) {
            self.isUserInteractionEnabled = true
        }
    }
}

###How to use:###

@IBAction func pressedButton(_ sender: UIButton) {
     sender.preventRepeatedPresses()
     // do the job
}

Upvotes: 11

matt
matt

Reputation: 535566

No timer needed. No disabling needed.

When the button is tapped, store the current Date in an instance property. When the button is tapped again, just before you store the current Date in that same instance property, subtract the old Date from the new Date. If they are too close together, do not also perform the button's action, whatever it is.

In other words, you take action only if the two timestamps are sufficiently far apart.

This is called debouncing, and is a common technique. Do a search and you will find much discussion of it here.

EDIT: No Date storage needed, nowadays! Just use the Combine framework's built-in debounce operator.

Upvotes: 14

Henrik Larsson
Henrik Larsson

Reputation: 51

If you want this to apply to all buttons in your app, you can extend UIButton as such:

extension UIButton {
    override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        
        self.isUserInteractionEnabled = false

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.isUserInteractionEnabled = true
        }
    }
}

If you have buttons that you do want to be able to hammer repeatedly, you could either use a specific extension class for those particular buttons, or create a custom extension class for all buttons with an @IBInspectable of type Bool to set whether or not a specific button should deactivate temporarily after being pressed.

Upvotes: 0

derdida
derdida

Reputation: 14904

You dont need a timer, could wait a few seconds and activate the button again.

So:

var ButtonIsActiv = false

func buttonPressed(button: UIButton) {
    if ButtonIsActiv == false {
       // do something with your button
       ButtonIsActiv = true

       // after 3 seconds, activate it again

       DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
           ButtonIsActiv = false
       }

    } else {
       // your button is not activated
    }
}

Upvotes: 4

boraseoksoon
boraseoksoon

Reputation: 2455

How about this?

//
//  ViewController.swift
//  SwiftButtonStopStackOverflow
//
//  Created by Seoksoon Jang on 29/09/2016.
//  Copyright © 2016 Seoksoon Jang. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet var testButton: UIButton!
    @IBAction func clickAction(_ sender: AnyObject) {

        self.testButton.isEnabled = !self.testButton.isEnabled

        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            self.testButton.isEnabled = !self.testButton.isEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

Upvotes: 1

Sealos
Sealos

Reputation: 562

You could have a Bool in your controller that says if the button is actionable or not, and when the button is pressed, set the Bool, and schedule a TimeInterval, like so: Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(<YourMethod>), userInfo: nil, repeats: false)

Another strategy would be after the button is pressed, disable the interaction with the property userInteractionEnabled and then with the Timer, enable the property again.

Upvotes: 0

Related Questions