Bor is
Bor is

Reputation: 75

Attempt to present which is already presenting

All I wand to do is to invoke new controller over the current context when I'm touching for a long time(UILongPressGestureRecognizer). Here the code:

class ViewController: UIViewController {

@IBOutlet weak var button: UIButton!{
    didSet{
        button.addGestureRecognizer(UILongPressGestureRecognizer(target: self,
                                                                 action: #selector(settingTheButton(_:))))
    }
}

func settingTheButton(_ recognizer: UILongPressGestureRecognizer){
    print("touchTheColorButton was called") // here the method invokes twice
    if let button = recognizer.view as? UIButton{
        performSegue(withIdentifier: "setTheButtonColor", sender: button)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?){
    if segue.identifier == "setTheButtonColor", let vc = segue.destination as? SettingsForButtonVC, let senderButton = sender as? UIButton {
        vc.button = senderButton
    }
}
}

I don't understand. Why has the method settingTheButton invoked twice? It doesn't interfere or harm a program but it really disturb me.

Console: touchTheColorButton was called touchTheColorButton was called 2017-02-18 01:28:12.499552 Test[1042:223223] Warning: Attempt to present on which is already presenting

Upvotes: 0

Views: 779

Answers (2)

migues
migues

Reputation: 92

Expanding on the solution given by @Marco Santarossa (can't comment). This will work correctly:

func settingTheButton(_ recognizer: UILongPressGestureRecognizer){

    // Get the button just as you have done
    if let button = recognizer.view as? UIButton{

        // Take only the `.ended` event, discarding any other ones
        if recognizer.state == .ended {
            performSegue(withIdentifier: "setTheButtonColor", sender: button)
        }
    }
}

The issue with your code is that settingTheButton(_ recognizer:) is called once per every time an event is triggered in the UILongPressGestureRecognizer, not just when releasing the button which is the behavior you probably expect. The two events are .began (once the press has been recognized as a long press) and .ended (after releasing the long press).

Simply ignoring on the two will make your code be called just once, additionally it makes more sense on .ended due to the fact that you probably want the new screen to appear after releasing the button instead of during the button press.

NOTE: Just so you know, it's better to specifically only allow .ended since there is at least one other common event that can be triggered apart from those two that I know of. If you are on a force touch device, .changed will be called many times, once for each extremely slight change of pressure that is made.

Upvotes: 1

Marco Santarossa
Marco Santarossa

Reputation: 4066

Your problem is that you're not listening the state of the gesture.

Change to:

func settingTheButton(_ recognizer: UILongPressGestureRecognizer){
    guard recognizer.state == .ended else { return }

    print("touchTheColorButton was called") // here the method invokes twice
    if let button = recognizer.view as? UIButton{
        performSegue(withIdentifier: "setTheButtonColor", sender: button)
    }
}

I didn't test properly. Let me know if it doesn't work before downvoting.

Upvotes: 2

Related Questions