Search code examples
iosobjective-ciphonexcodeuistoryboardsegue

How to unwind through multiple views without displaying intermediate views


Assume we have three view controllers: 1, 2, and 3. Using the storyboard, it's pretty simple to unwind from view controller 3 to view controller 1 using an unwind segue. However, when unwinding, view controller 2 is briefly visible before view controller 1 is displayed. Is there any way to get from 3 to 1 without displaying 2 again?

View Controller 1:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"one did appear");
}

- (IBAction)goToTwo:(id)sender {
  NSLog(@"#### segue to two");
  [self performSegueWithIdentifier:@"TwoSegue" sender:self];
}

- (IBAction)unwindToOne:(UIStoryboardSegue *)sender {
}

View Controller 2:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"two did appear");
}
- (IBAction)goToThree:(id)sender {
  NSLog(@"#### segue to three");
  [self performSegueWithIdentifier:@"ThreeSegue" sender:self];
}

View Controller 3:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"three did appear");
}

- (IBAction)unwindToOne:(id)sender {
  NSLog(@"#### unwind to one");
  [self performSegueWithIdentifier:@"OneSegue" sender:self];
}

This produces the following log messages:

  • one did appear
  • segue to two
  • two did appear
  • segue to three
  • three did appear
  • unwind to one
  • two did appear
  • one did appear

I've tried using custom segues and disabling animation. Although removing animation makes view controller 2 appear for an even shorter period of time, it still appears. Is there any way to program this behavior?

Screenshot of storyboard:

enter image description here


Solution

  • Josh's answer led me to a solution. Here's how to accomplish this:

    Create a root UINavigationController, and assign it to a class that extends UINavigationController and overrides the segueForUnwindingToViewController:fromViewController:identifier method. This could be filtered by the identifier if desired.

    CustomNavigationController:

    - (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
      return [[CustomUnwindSegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController];
    }
    

    Create a custom push segue, that behaves like a modal segue, but utilizes our navigation controller. Use this for all "push" segues.

    CustomPushSegue:

    -(void) perform{
      [[self.sourceViewController navigationController] pushViewController:self.destinationViewController animated:NO];
    }
    

    Create a custom unwind segue, that uses the navigation controller to pop to the destination. This is called by our navigation controller in the segueForUnwindingToViewController:fromViewController:identifier method.

    CustomUnwindSegue:

    - (void)perform {
      [[self.destinationViewController navigationController] popToViewController:self.destinationViewController animated:NO];
    }
    

    By utilizing a combination of these patterns, the second view controller never appears during the unwind process.

    New log output:

    • one did appear
    • #### segue to two
    • two did appear
    • #### segue to three
    • three did appear
    • #### unwind to one
    • one did appear

    I hope this helps someone else with the same issue.