How to "cancel" a UIStoryBoardSegue

31,823

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

  1. Create two identical cell with different identifiers. Eg: "cellWithSegue" and "cellWithoutSegue".
  2. Connect the first cell ("cellWithSegue") with the segue you want to display.
  3. Do not connect the second cell with any segue.

Table View

  1. On cellForRowAtIndexPath, implement a logic to determine if the cell should be linked a segue or not.
  2. 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];
}
  1. 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.

Share:
31,823

Related videos on Youtube

Fabio B.
Author by

Fabio B.

http://www.linkedin.com/in/fabiobozzo

Updated on July 09, 2022

Comments

  • Fabio B.
    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
      Joshcodes almost 11 years
      If you are using iOS 6 or greater, see my answer below for a proper way to handle this.
  • Dan Waterbly
    Dan Waterbly over 12 years
    This 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
    JordanC over 12 years
    Agreed this is not needed. Just manually call performSegueWithIdentifier and you will not need to override UIStoryboardSegue
  • Leonardo
    Leonardo over 12 years
    in 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
    Senad Uka over 12 years
    It's the simplest way I have found :) Cool! Thank you!
  • Dave
    Dave about 12 years
    Great tip! Perfect solution in my case.
  • Phil Hale
    Phil Hale about 12 years
    It's my preferred solution as well. Thanks!
  • Patrick
    Patrick almost 12 years
    This is by far the most robust solution and still very easy. Much thanks. I would up vote it again if I could.
  • lhunath
    lhunath almost 12 years
    Probably the most generically applicable solution.
  • mcrute
    mcrute almost 12 years
    This just doesn't work at all. You can't dismiss the view controller because you're running before the segue happens.
  • mcrute
    mcrute almost 12 years
    A 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
    Hank Brekke almost 12 years
    For 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
    Amandir over 11 years
    A simple and fast fixed, good explanation and sources - I wish every answer would be this great
  • jweyrich
    jweyrich over 11 years
    Although this will not work as expected if you still want the cell to be selected without performing the segue.
  • JK Laiho
    JK Laiho over 11 years
    jweyrich: 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
    jweyrich over 11 years
    Imagine 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 property inSelectionMode and conditionally change accessoryType from Disclosure to Mark. Consequently I wanted the cell to be visually selected, without performing the segue. Both seem to require didSelectRowAtIndexPath to be called. The easiest solution I found was to link the segue directly to the controller and call performSegueWithIdentifier conditionally.
  • Sten Petrov
    Sten Petrov almost 11 years
    wow, 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
    devios1 almost 11 years
    -1 This is a horrible solution. "Create two identical cells" is a direct violation of the DRY principle.
  • AlanObject
    AlanObject over 10 years
    Looking 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
    Radu Simionescu about 10 years
    and what makes you think this is "tidy"?
  • zirinisp
    zirinisp about 10 years
    I don't any more. I was not aware of the accepted solution at the time.
  • Cœur
    Cœur almost 9 years
    Note 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 in prepareForSegue:sender:, as the cell may be recreated without user interaction disabled.