genericperson
genericperson

Reputation: 63

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

Upvotes: 2

Views: 1317

Answers (3)

Jeetendra
Jeetendra

Reputation: 1

better to delete TouchUp Inside on Button.

Upvotes: 0

rkyr
rkyr

Reputation: 3251

For me this method was helpful:

override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
  for window in UIApplication.sharedApplication().windows {
    if let window = window as? UIWindow {
      if let sub = window.subviews.first as? UIAlertView {
        return false
      }
    }
  }
  return navigationController?.topViewController == self
}

I just move this method to RootViewController, from which every controller in my main hierarchy was inherit. Notice that in method I'm also check if there is UIAlertView presenting.

Upvotes: 0

Steve Wilford
Steve Wilford

Reputation: 9012

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
    }

}

Upvotes: 3

Related Questions