Jdmoreira Jdmoreira - 4 months ago 33
Swift Question

performSegueWithIdentifier pushes two ViewControllers when pressed 2x fast

I have a complex app when I sometimes need to use performSegueWithIdentifier. I can't replace this with IB triggers, must be done programatically! Sometimes I call them in UIButton's actions or even in delegate methods as collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)

The problem is that if I trigger the events twice and fast, for example by double tapping an UIButton, I get the destinationViewController pushed twice into the navigationController's stack, which is not what I want. I only want it to be pushed once. prepareforSegue gets call twice as well.

Example:

func addDeal(sender: UIBarButtonItem) {
self.performSegueWithIdentifier("segueDeal", sender: sender)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueDeal") {
(segue.destinationViewController as! DealVC).deal = self.deal
}
}

Answer

Disable the button when this method is called, this will prevent it from being tapped multiple times.

func addDeal(sender: UIBarButtonItem) {
    sender.enabled = false
    self.performSegueWithIdentifier("segueAddDeal", sender: sender)
}

You will also have to re-enable it when your view re-appears for example:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    someReferenceToTheButton.enabled = true
}

Hair-Brained Alternative

I've just knocked this together in a sample project as a "more generic way" as requested in the comments. It "works" in the basic use case as shown but I honestly have no idea if it will work in more complex situations. I'm also not particularly happy with it, feels hacky and fragile.

class ViewController: UIViewController {

    private var preparingForSegue = false

    override func viewWillAppear(animated: Bool) {
        preparingForSegue = false
        super.viewWillAppear(animated)
    }

    @IBAction func pushOn(sender: AnyObject) {
        // Provided as an example of an attempt to perform 2 segues in quick succession
        self.performSegueWithIdentifier("DoPush", sender: self)
        self.performSegueWithIdentifier("DoPush", sender: self)
    }

    override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
        if !preparingForSegue {
            super.performSegueWithIdentifier(identifier, sender: sender)
        }
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        super.prepareForSegue(segue, sender: sender)
        preparingForSegue = true
    }

}