How to "cancel" a UIStoryBoardSegue
Solution 1
If you are targeting iOS 6 or greater, then my knowledge of the cleanest way to do this is the following:
-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if([identifier isEqualToString:@"show"])
{
NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
Blocco *blocco = [self.fetchedResultsController objectAtIndexPath:selectedRowIndex];
return [blocco meetRequiredConditions];
}
return YES;
}
Where there is a method
-(BOOL) meetsRequiredConditions;
Defined on your Blocco class returns YES if the "couple of things" which permit a drill-down are valid.
Solution 2
I don't know if it is the right way to do it but I discovered a workaround.
From the storyboard I associate(control+click) a segue from the status bar in the view controller. Give the segue an ID (for example: switchSegue).
Now, from an action in your code (in my code I use a button), I call:
[self performSegueWithIdentifier:@"switchSegue" sender:sender];
That way you can control if your segue is performed or not. Try tutorials that helped me from here and here
Hope this helps.
Solution 3
I am using an much easier and tidy approach.
Storyboard
- Create two identical cell with different identifiers. Eg: "cellWithSegue" and "cellWithoutSegue".
- Connect the first cell ("cellWithSegue") with the segue you want to display.
- Do not connect the second cell with any segue.
Table View
- On cellForRowAtIndexPath, implement a logic to determine if the cell should be linked a segue or not.
- For cells that should be linked with the segue use the "cellWithSegue" identifier, for the rest the "cellWithoutSegue".
This way looks a lot easier to implement and also does not alter the way segues are supposed to work.
Solution 4
I may be wrong here, but after struggling myself with this, I just disabled the cell's user interaction on the cells where I didn't want the seque triggered (in cellForRowAtIndexPath:). Seems to work perfectly, and it's only 1 line of code!
cell.userInteractionEnabled = NO;
Solution 5
The easiest solution is to create manual segue in story board and use that as seen below.
[self performSegueWithIdentifier:@"loginSuccessSegue" sender:self];
Or
@Fabio: I was trying to get a solution for same kind of use-cases and I almost found a solution.
Use-cases 1. Stop segue transition conditionally 2. Change destination viewController conditionally
Solution:
Use "Custom" segue. Follow below steps to create Custom segue 1. Create a subclass of UIStoryboardSegue "MyCustomSegue.h"
@interface MyCustomSegue : UIStoryboardSegue
@end
"MyCustomSegue.m"
Override initWithIdentifier for implementing use-case 1 and 2 If you return nil, segue will be cancelled/no action will be taken You instantiate your ViewController and set that as a destination. You can set destination as your old xib file also.. that code is commented, but I ensured that will work.
@implementation MyCustomSegue
- (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination{
UIStoryboard *storyBoard= [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];
UIViewController *viewController = [storyBoard instantiateViewControllerWithIdentifier:@"testIdentifier"];
// MyViewController* viewController= [[MyViewController alloc]initWithNibName:@"MyViewController" bundle:nil];
return [super initWithIdentifier:identifier source:source destination:viewController];
}
- You must override "perform".
You can implement use-case 1 here also..
- (void)perform {
// if either source or destination is nil, stop
if (nil == self.sourceViewController || nil == self.destinationViewController) return;
// return; //No Action. Segue will be cancelled
UINavigationController *ctrl = [self.sourceViewController navigationController];
[ctrl
pushViewController:self.destinationViewController
animated:YES];
}
Hope this helps. Plz write if you are not clear.
Related videos on Youtube
Comments
-
Fabio B. almost 2 years
Does anyone know how to "stop" a segue transition conditionally:
My table view cells represent products which can be viewed in a drill-down "detail" view... or cannot! (It depends on a couple of things)
Now my App considers all products "unlocked":
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow]; ListaProdottiController *prodottiViewController = [segue destinationViewController]; prodottiViewController.blocco = [self.fetchedResultsController objectAtIndexPath:selectedRowIndex]; }
How can I cancel the row selection => drilldown, at this point?
-
Joshcodes almost 11 yearsIf you are using iOS 6 or greater, see my answer below for a proper way to handle this.
-
-
Dan Waterbly over 12 yearsThis seems to be a lot of extra work than what is needed. Wouldn't it be easier to choose which seque you wanted to invoke (or not) based on what table cell is selected? You would not have create a custom segue and your view controller would lose its dependency on other view controllers. You can request that a seque be perfomed in code by using: [self performSegueWithIdentifier:@"SegueIdentiferName" sender:self];.
-
JordanC over 12 yearsAgreed this is not needed. Just manually call performSegueWithIdentifier and you will not need to override UIStoryboardSegue
-
Leonardo over 12 yearsin my opinion it is not a lot of code, it is just one class than you can reuse for other segue and controllers by use of categories and protocols for example. I found it much better than writing lot of it-then-else case for deciding which button or cell has been pressed in the xib.
-
Senad Uka over 12 yearsIt's the simplest way I have found :) Cool! Thank you!
-
Dave about 12 yearsGreat tip! Perfect solution in my case.
-
Phil Hale about 12 yearsIt's my preferred solution as well. Thanks!
-
Patrick almost 12 yearsThis is by far the most robust solution and still very easy. Much thanks. I would up vote it again if I could.
-
lhunath almost 12 yearsProbably the most generically applicable solution.
-
mcrute almost 12 yearsThis just doesn't work at all. You can't dismiss the view controller because you're running before the segue happens.
-
mcrute almost 12 yearsA better approach would be connect the thing that triggers the segue to an
IBAction
that determines if the segue should occur and triggers[self performSegueWithIdentifier:@"Whatever" sender:self]
-
Hank Brekke almost 12 yearsFor some reason it still works when the segue is presented modally though, even though it doesn't seem like it would. It worked in my application that I discontinued. It may not work now that 5.0.1 and 5.1 are released. There is a
shouldPerformSegueWithIdentifier:sender:
method in iOS 6 but nothing more can be explained because of NDA. -
Amandir over 11 yearsA simple and fast fixed, good explanation and sources - I wish every answer would be this great
-
jweyrich over 11 yearsAlthough this will not work as expected if you still want the cell to be selected without performing the segue.
-
JK Laiho over 11 yearsjweyrich: True. Programmatically doing the cell selection in willSelectRow... (perhaps with a timed unselection) is still possible I think, and in any case, you'd want to give the user some type of indication of why the cell tap isn't doing what they expect.
-
jweyrich over 11 yearsImagine a picker, but on its own screen. The user taps an item on the table, it calls a delegate passing the selected item, which then handles it and pops the controller. This required me to extend
UITableViewController
, add a propertyinSelectionMode
and conditionally changeaccessoryType
from Disclosure to Mark. Consequently I wanted the cell to be visually selected, without performing the segue. Both seem to requiredidSelectRowAtIndexPath
to be called. The easiest solution I found was to link the segue directly to the controller and callperformSegueWithIdentifier
conditionally. -
Sten Petrov almost 11 yearswow, this is the most tedious way around - now you have to maintain the looks of two cell types, the code that determines which one to show them instead of
if (this) doSegue()
kind of thing. -
devios1 almost 11 years-1 This is a horrible solution. "Create two identical cells" is a direct violation of the DRY principle.
-
AlanObject over 10 yearsLooking at the Apple reference it seems this is how it is intended to be used. In my case if the segue is blocked I would like to do a dialog box. One thing at a time though.
-
Radu Simionescu about 10 yearsand what makes you think this is "tidy"?
-
zirinisp about 10 yearsI don't any more. I was not aware of the accepted solution at the time.
-
Cœur almost 9 yearsNote that this solution is OK for
cellForRowAtIndexPath:
but is not enough when used in a different method, for instance when attempting to prevent double-triggering inprepareForSegue:sender:
, as the cell may be recreated without user interaction disabled.