How to check if segue is in progress - ios

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.

Related

Avoid performSegue multiple times

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

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.

How to perform actions based on segue

Here's what I'm trying to do:
I have 2 views, Settings and Main Screen.
I've coded a segue to occur when a button in Settings is pressed, taking the view to the Main Screen. The identifier is "reset".
I'm trying to have the Main Screen perform a series of actions if this segue is triggered, but I can't find the function or way to do this.
Any help on how to implement this? I'd like it to trigger when the segue occurs.
You can pass arguments to the main screen in the prepareForSegue function in the settings page. Then in your main screen you can put in checks in your viewWillAppear function to handle them as you see fit.
Example:
In Settings:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "reset") {
// pass data to next view
let viewController:ViewController = segue!.destinationViewController as MainViewController
viewController.settings = settings // where settings is what you want to pass
}
}
In Main Screen:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if self.settings { // Do something }
}
If you're wanting something to happen when the segued view controller is triggered you can do it in a couple of places.
If you want the event to happen at the time the segue is fired, perform your code inside of prepareForSegue. There you can check the UIStoryboardSegue object's identifier field for "reset" and if true, call your logic.
If you want it to happen when the destination view controller loads or appears, do it inside of its viewDidLoad or viewDidAppear methods. In your case those methods would exist on the "Main Screen" view controller class.

Is it possible to delay and/or cancel a Show Detail segue for UISplitViewController?

I would like to use a UISplitViewController to show a list of connectable devices in the master view, and to show information retrieved from a selected device in the detail view.
To do that, when the user taps on a device, I need to attempt to connect to that device. If unsuccessful, there'd be nothing to show and I would display an error message. If successful, I'd read data from the device and display that in the detail view.
The Show Detail segue from the UISplitViewController seems to automatically fire immediately after an entry in the master view is tapped. Can I intercept this somehow to add the necessary logic for connection/read? If not, what is an alternative method for me to do this?
In your ViewController you can override shouldPerformSegueWithIdentifier
class Foo: UIViewController {
func doAsynchStuff(completion: (canProceed:Bool)->()) {
// ...
}
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if everythingOK {
return true
} else {
// do your stuff
doAsynchStuff({ (canProceed) -> () in
if canProceed {
self.performSegueWithIdentifier(identifier, sender: sender)
} else {
// present error message
}
})
return false
}
}
}
If you return true the segue will be execute, otherwise you:
run your asynchronous code
invoke performSegueWithIdentifier and when the asynch function has completed you decide what you do looking at the result
return false to cancel the current segue

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