Avoid performSegue multiple times - ios

I have more than 20+ buttons in my application. For these buttons if I click twice it performSegue twice and opens the viewcontroller twice, throughout my application in swift iOS?
self.performSegue(withIdentifier: "toViewController", sender: nil)

You can attach a tag to each button and define an array of segues
self.performSegue(withIdentifier:segues[sender.tag], sender: nil)
or simply make the button the source of the segue , if you're not willing to override prepareForSegue

You could declare a property
var isSegueEnabled = true
Then implement the method to control whether the segue should be performed
func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return isSegueEnabled
}
In prepare(for segue set the property to false
isSegueEnabled = false
At some point after the presented view controller has been dismissed set isSegueEnabled back to true

Related

performSegue creates two view controllers

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
self.delegate = segue.destination as? MenuViewController
print("check_1", self.delegate)
}
#IBAction func openMenu(_ sender: Any) {
performSegue(withIdentifier: "openMenu", sender: sender)
print("check_2", self.delegate)
}
My main ViewController updates values while MenuViewController displays these values. Each time ViewController values are updated, it calls self.delegate.updateValues in MenuViewController. I transition between the two ViewControllers through buttons.
My problem is that it seems like the MenuViewController displayed is a different object than the one stored in self.delegate inside ViewController. Printing the check statements:
check_1 Optional(<Menu.MenuViewController: 0x10161ca10>)
check_2 Optional(<Menu.MenuViewController: 0x10161ca10>)
check_1 Optional(<Menu.MenuViewController: 0x10161dd10>)
May I ask how do I make sure only one instance of MenuViewController is created and stored in self.delegate?
When you add a segue to a storyboard, if you hook up the segue to a specific button/IBAction, you don't need to call performSegue manually, it will be automatically called for you.
You have 2 segues executed, since both the storyboard executes the segue and then you also do it from code by calling performSegue.
performSegue should only be used when your segue isn't directly hooked up to a UI event or if you need to conditionally perform a segue - such as when you have a login button, where depending on the network response, you might execute an error or a login segue.

How to check if segue is in progress

I sometimes manually invoke segue using
self.performSegue(withIdentifier: "SegueID", sender: self)
The problem is sometimes the function containing above line is invoked twice in succession. This causes segue animation to be aborted in the middle and restart, which looks weird. How do I check if segue animation is already in progress?
The method shouldPerformSegue(withIdentifier:sender:) is called automatically to check if the segue you try to execute should really be performed:
func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool
Determines whether the segue with the specified identifier should be performed.
The Bool return value determines if the segue will be performed:
true if the segue should be performed or false if it should be ignored.
Try to incorporate one of the following checks inside shouldPerformSegue(withIdentifier:sender:) and return true or false depending on the outcome:
Check if the segue's destination view controller is already loaded into memory with isViewLoaded?
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
// check if the destination view controller is not yet loaded into memory
if !target.isViewLoaded {
return true // perform the segue
}
return false // do not perform the segue
}
Check if the segue's destination view controller is already presented with isBeingPresented?
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
// check if the destination view controller is not presented yet
if !target.isBeingPresented {
return true // perform the segue
}
return false // do not perform the segue
}
You need to get the target ViewController depending on how your app is set up. You can identify it with a identifier or by checking the array of presented or child view controllers on your current view controller for an already existing view controller of the target class type. I would require more information to give a more specific solution.

An action triggered automatically after a programmatic segue

I use this simple code to segue:
self.performSegue(withIdentifier: "showSeqTwo", sender: self)
After this segue, I need a timer in the controller to launch automatically. The problem is that normally the timer is supposed to be launched by pressing a play button, and then paused and relaunched at will. The user can also swipe to this controller, in which case the timer should not be triggered. Can I send some kind of a signal with this particular segue to programmatically "push" the button and launch the timer?
You can send a variable that is read in the viewDidLoad() function of the second viewController, and determine if the play button should activate.
Add a function to the bottom of your first viewController like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSeqTwo" {
let destination = segue.destination as! NameOfSecondViewControllerClass
destination.segueFlag = 1
}
}
Inside the second viewController, add the following variable into the class:
var segueFlag = 0
Inside the viewDidLoad() of the second viewController, add the following code
if segueFlag == 1 {
// Add function to start play button here
}

segue storyboard condition issue

I have this two controller liked by segue:
My problem is that:
when I tap on selected table view'cell in general I have to follow the segue and so go to other controller , but if a specific condition is true I have to show ad alert view and so I don't follow the segue and so don't go to those controller.
In this way when I tap on selected cell I go always on the other controller.
How do I solve this problem?
if you override this function segue wont continue if you return false, this gives you the oportunity to show a warning under certain conditions, after that you can performSegueWithIdentifier("segueidentifier", sender: self), and your good to go.
override func shouldPerformSegueWithIdentifier(identifier: String!, sender: AnyObject?) -> Bool {
if identifier == "segueidentifier" {
//show warning and perform segue with this identifier on the accept button listener.
return false
}
}
return true
}
You can add a segue from one controller to another without specifying exact view which triggers the segue. Then just check your condition in didSelectRow method and call performSegue method if you need to show new view controller:
if condition {
performSegue(withIdentifier: "MySegueIdentifier", sender: nil)
}
else {
//show ad here
}
To add a segue between controllers just drag with right mouse button from one controller to another and pick "Show".
Then select segue and add a string identifier for it:
Make sure you use the same string in the code when you call performSegue method.

Calling performSeguewithIdentifier doesn't call shouldperformseguewithIdentifier

I have two view controllers. On view controller1 I have the following:
a segue that takes me to viewcontroller2 - this segue is named "showme" and is attached to the viewcontroller
an IBAction for a UIButton
In my code I have the following for the button press action
#IBAction func buttonPress(sender: AnyObject) {
println("button pressed")
performSegueWithIdentifier("showme", sender: self)
}
I also have the following method:
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
println("Should performing....")
return true
}
For some reason the shouldPerformSegueWithIdentifier function is never called. If however, I add the segue directly on the UIButton to ViewController2 it is.
I have confirmed that calling it direction within my button action works (see below), but this is not what I understand to be the way it works. The same is true for prepareforSegue..
#IBAction func buttonPress(sender: AnyObject) {
println("button pressed")
if (shouldPerformSegueWithIdentifier("showme", sender: self)){
performSegueWithIdentifier("showme", sender: self)}
}
This behaviour is perfectly natural, for the following reasons:
1) shouldPerformSegueWithIdentifier is used to make sure that a segue that has been set up in Storyboards should be triggered, so it only gets called in the case of Storyboard Segues and gives you the chance to not actually perform the segue.
2) When you call performSegueWithIdentifier yourself, shouldPerformSegueWithIdentifier is not called because it can be assumed that you know what you are doing. There would be no point in calling performSegueWithIdentifier but then return a NO from shouldPerformSegueWithIdentifier.
#nburk answer is absolutely correct.
However I understand that in some situations it could be useful if shouldPerformSegueWithIdentifier:sender: would be called anyway, also when a call to performSegueWithIdentifier:sender: is made in code.
For instance we want to make some validations to decide whether performing a segue or not and we want to keep this logic in a single place and not duplicating all over the place conditions like the following:
if (self.shouldPerformSegue) {
[self performSegueWithIdentifier:identifier sender:sender];
}
This can be easily achieved overriding performSegueWithIdentifier:sender: as follows:
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if ([self shouldPerformSegueWithIdentifier:identifier sender:sender]) {
[super performSegueWithIdentifier:identifier sender:sender];
}
// otherwise do nothing
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
return self.shouldPerformSegue;
}
This way you can use shouldPerformSegueWithIdentifier:sender: to define your logic to allow/deny both IB and code triggered segues.
As the answer above. If you call performSegueWithIdentifier then shouldPerformSegueWithIdentifier is not called.
As an example:
Lets say you have an embedded segue inside a container view in order to show some images that you can swipe through. And embedded segues gets fired right away when you VC has loaded. But if you would have to download the images from an remote API your app would crash since there wouldnt be any images to display in the embedded segue/container view.
In this case shouldPerformSegueWithIdentifier would be needed.
You could setup a boolean value that you check in shouldPerformSegueWithIdentifier if its false return false and your segue wont be fired. And once your app has downloaded the images you could call performSegueWithIdentifier
Thanks #tanzolone for the perfect solution. Rewrote code on Swift 5.
To forcefully call shouldPerformSegue before performingSegue, you can override performingSegue in you class:
override func performSegue(withIdentifier identifier: String, sender: Any?) {
if shouldPerformSegue(withIdentifier: identifier, sender: sender) {
super.performSegue(withIdentifier: identifier, sender: sender)
}
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
// Your code (return true if you want to perform the segue)
}
if you're using this code you need to remove;
[self performSegueWithIdentifier:name sender:sender];

Resources