My question is, how do I get the following custom unwind segue to work on a device with a version prior to iOS 9 as well as on a device running iOS 9?
I have a Custom Segue showing a view controller, and then have a corresponding Custom Unwind Segue. This code has worked fine in iOS 8, and is implemented by creating subclasses of UIStoryboardSegue and implementing the perform method. Then I override the following method in my custom Navigation Controller:
- (UIStoryboardSegue *) segueForUnwindingToViewController: (UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier
{
UIStoryboardSegue *segue;
if([fromViewController isKindOfClass:[MyViewController class]]){
segue = [[CustomSegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController]; //Custom Unwind Segue
}
else{
UIStoryboardSegue *unwindSegue = [super segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier]; //Normal Unwind Segue
segue = unwindSegue;
}
return segue;
}
In iOS 9, segueForUnwindingToViewController is deprecated. It still works for the MyViewController CustomSegue; however, the default unwind segue no longer works for any other unwind segue. Although calling the method on super returns an unwind segue, the segue never occurs, the view controller is never popped, and the user can never go back to the previous screen. So just to be clear, if I use a regular show segue, the corresponding unwind segue calls the deprecated method, which calls the method on super, and does not work.
I watched the WWDC talk on storyboards, and I was able to fix this issue in iOS 9 by a) no longer overriding this method in my custom Navigation Controller, and b) going into the storyboard, clicking on the custom segue, and entering in CustomSegue as the Segue Class. Unfortunately, since I am targeting iOS 7, I get the warning "Only Custom segues support class names prior to iOS 9", and the custom unwind segue now does not work for iOS 7 or iOS 8!
After a lot of trial and error, I found a workaround. I noticed that when you override segueForUnwindingToViewController, unwindForSegue:towardsViewController is no longer called, which is the problem. FYI, Apple's note in UIViewController for segueForUnwindingToViewController says:
Deprecated. Returns a segue that will unwind from the source to destination view controller via the unwindForSegue:towardViewController: method. When performing an unwind segue defined in a storyboard, if any view controller along the unwind path has overridden this method and returns non-nil, the runtime will use that segue object instead of constructing an instance of the class specified in Interface Builder.
The portion of the statement in bold does not seem to be implemented, i.e. if you override this method but return nil, unwindForSegue:towardViewController is still not called and the segue does not occur.
In order to get around this problem, I was able to edit the segueForUnwindingToViewController in my custom Navigation Controller:
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier
{
UIStoryboardSegue *segue;
if([fromViewController isKindOfClass:[MyViewController class]]){
segue = [[CustomSegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController]; //Custom Unwind Segue
}
else{
if([[UIDevice currentDevice].systemVersion floatValue] < 9.0){
return [super segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier]; //Normal Unwind Segue
}
else{
[super unwindForSegue:segue towardsViewController:toViewController];
return nil;
}
}
return segue;
}
UPDATE: Calling [super segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier]; seems to still work for modal unwind segues. The issue I described seems to happen for show segues. Consequently, if your device runs on a version < 9.0 or if the segue is modal, you should still call the old method. If you call [super unwindForSegue:segue towardsViewController:toViewController]; when the segue is modal, the segue will not work/occur.
I slightly modified your code.
I don't have to consider whether push or modal.
It seems to work fine.
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier
{
UIStoryboardSegue *segue;
if ([fromViewController isKindOfClass:[MyViewController class]]) {
segue = [[CustomSegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController]; //Custom Unwind Segue
}
else {
if ([super respondsToSelector:#selector(unwindForSegue:towardsViewController:)]) {
[super unwindForSegue:segue towardsViewController:toViewController];
}
segue = [super segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier];
}
return segue;
}
Related
I have a Show segue that is embedded in a UITabBarController. I want to prevent an unwind segue when I tap the currently selected tab unless a certain condition is met. I've tried using shouldPerformSegueWithIdentifier and canPerformUnwindSegueAction but neither appear to be triggered when unwinding in this way.
Not sure what you mean by unwind segue on a tab bar, but if you want to prevent a tab change, there is a delegate function on UITabBarController for that purpose.
Add the protocol to your tab bar class.
#interface YourTabbarViewController () <UITabBarControllerDelegate>
#end
Assign the delegate, then later implement the function.
#implementation YourTabbarViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if (preventTabChange)
return NO;
return YES;
}
UPDATE
OK, assuming you have set up relevant parts as on this picture, and you want to prevent the unwind from B to A if certain conditions are met. My solution as described above will work.
As you will get a query/notification whenever the Navigation Controller is about to become active, you could create your own sub-class of that to hold whatever information you need to decide if it should be allowed to show or unwind from a sub-view controller. In that case your prevention could look like this (expanding the shouldSelectViewController above):
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if ([viewController isKindOfClass:[YourNavigationController class]]) {
if ([(YourNavigationController *)viewController preventUnwind])
return NO;
}
return YES;
}
Note that I purposely chose preventUnwind as a flag in your custom class to say what to do. This will default to NO when you move to the view controller, and thus allowing that.
Don't forget to set YourTabbarViewController as the class for the Tabbar View Controller and YourNavigationController as the Navigation Controller in the picture.
I have an app, that works fine under iOS 7, but when built for iOS 8 the unwind segues are not working.
I created a new project and added a modal (navigationcontroller with tableviewcontroller)and tried to use an unwind modal. Unfortunately it doesn't work either. The methods that are being unwind to, are in the desination view controller. The unwind segue is created through the storyboard (a Navigationbar button in the tableviewcontroller) When I tap the button, nothing happens. There is no log output and the modal does not disappear. It also only seems to affect modal segues. push/popover are unwound normally.
Has anyone had a similar problem and has an Idea how I could solve it?
Apple has FIXED this bug in iOS 8.1
Temporary solutions for iOS 8.0
The unwind segue will not work only in next situation:
View structure: UITabBarController -> UINagivationController -> UIViewController1 -> UIViewController2
Normally (in iOS 7, 8.1), When unwind from UIViewController2 to UIViewController1, it will call viewControllerForUnwindSegueAction in UIViewController1.
However in iOS 8.0 and 8.0.x, it will call viewControllerForUnwindSegueAction in UITabBarController instead of UIViewController1, that is why unwind segue no longer working.
Solution: override viewControllerForUnwindSegueAction in UITabBarController by create a custom UITabBarController and use the custom one.
For Swift
CustomTabBarController.swift
import UIKit
class CustomTabBarController: UITabBarController {
override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController? {
var resultVC = self.selectedViewController?.viewControllerForUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender)
return resultVC
}
}
For old school Objective-C
CustomTabBarController.h
#import <UIKit/UIKit.h>
#interface CustomTabBarController : UITabBarController
#end
CustomTabBarController.m
#import "CustomTabBarController.h"
#interface CustomTabBarController ()
#end
#implementation CustomTabBarController
-(UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
return [self.selectedViewController viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
}
#end
==============================================================================
DO NOT USE ANY SOLUTIONS BELOW THIS POINT (they are out of date and just for reference)
Latest update on Sep 23
My new solution is pushing to a view that embedded in a navigation controller, and config that navigation controller to hide bottom bar on push(a tick box in IB). Then you will have a view looks like a modal view, the only different is the animate of pushing and popping. You can custom if you want
Updated: The solution below actually present the modal view under the tab bar, which will cause further view layout problems.
Change the segue type to Present As Popover will work only on iOS8 for iPhones, on iOS7 your app will crash.
Same here, to fix this, I set segue's presentation to current context(my app is for iphone only).
Default and full screen will not work.
[UPDATE: Bug fixed on iOS 8.1 beta but you'll need it for 8.0 and 8.0.2 support]
The only way I could make my unwind segue work was by mixing Aditya's and viirus' answers.
My setup going in:
[View Controller 1] > custom modal segue > [Navigation Controller] > root > [View Controller 2]
Unwind:
[View Controller 2] > custom unwind segue > [View Controller 1]
Fix:
Subclass the [Navigation Controller], add a property called sourceViewController and pass "self" to that property when prepare for segue is called when going from [View Controller 1] to [Navigation Controller]
In the [Navigation Controller] subclass .m override/add this two methods:
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
if ([self.sourceViewController canPerformUnwindSegueAction:action fromViewController:fromViewController withSender:sender]) {
return self.sourceViewController;
}
return [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
}
Then I override this in that [Navigation Controller] subclass only because I have a custom unwind segue:
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
return [fromViewController segueForUnwindingToViewController:toViewController
fromViewController:fromViewController
identifier:identifier];
}
This is a problem with iOS 8.0, 8.0.1, and 8.0.2. It was resolved in 8.1; unwind segues are calling the appropriate method now.
Note that on iOS 8, modally presented view controllers may not be automatically dismissed when performing an unwind segue, unlike iOS 7. To ensure it's always dismissed, you may detect if it's being dismissed and if not then manually dismiss it. These inconsistencies are resolved in iOS 9.0.
With iOS 8.4 running on iPhone, all of the modally presented segues with all presentation styles do dismiss upon unwind, except Over Full Screen and Over Current Context. That's also the case for iPad, with the addition of Form Sheet and Page Sheet also not auto-dismissing. With iOS 9, all presentation styles auto dismiss on both iPhone and iPad.
Yep it kinda happen to me too, I think for your case you have to subclass the UINavigationController and override the following:
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
for(UIViewController *vc in self.viewControllers){
// Always use -canPerformUnwindSegueAction:fromViewController:withSender:
// to determine if a view controller wants to handle an unwind action.
if ([vc canPerformUnwindSegueAction:action fromViewController:fromViewController withSender:sender])
return vc;
}
return [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
}
Same problem here. Unwind method is not called. Only happens when
using modal segue
Presentation is anything but "current context"
NavigationController is not extended (using default from storyboard)
Also happens in IOS8 GM Seed, therefore I think we need to find a workaround.
Sounds like a bug to me...
Extending UINavigationController and implementing viewControllerForUnwindSegueAction didn't help, as it is not fired. The only thing which gets fired is canPerformUnwindSegueAction() within the extended UINavigationController. Strange.
Woah there! I'm still getting user reports of getting stuck on a modal view in iOS 8.1.1 (on an iPad 3).
I'm jettisoning all this unwind from a modal view stuff. Just a good old-fashioned...
[self dismissViewControllerAnimated:NO completion:nil];
...works fine on all those various iOS 8.x.x versions.
It seems that both iOS 7.1 and iOS 8.1/8.2 create unwind segue from navigation controller however unwind segue is registered on a child controller inside of navigation controller.
So manually creating an unwind segue from the controller where it's registered in storyboard solves the problem.
#implementation RootNavigationController
- (UIStoryboardSegue*)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
return [toViewController segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier];
}
#end
I encountered the same problem when unwinding to a source view controller from a destination view controller. The destination was presented through a "show" segue from the source.
I was using iPhone simulator that shows iPhone 6, iOS8.3.
XCode 6.3.2
The solution of subclassing NavigationViewController worked for me. Here is the swift code which is essentially swift translation of Raul's answer. I am puzzled that if Apple has fixed it in iOS8.1 per Raul, how am I getting hit by it in 8.3.
var sourceViewController: UIViewController?
override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController? {
if(self.sourceViewController! .canPerformUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender!)){
return self.sourceViewController
}
return super.viewControllerForUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender)
}
I just ran into this problem, and after some digging discovered that with modal segues (at least ones with the default and fullscreen presentation modes), you can't rely on the normal unwind mechanism, but rather you have to call the presented UIViewController's dismissViewControllerAnimated method.
Steps to be followed:
Link the unwind segue to the button in Storyboard.
Create IBAction for the button and add the below code in it:
[self dismissViewControllerAnimated:NO completion:nil];
This should work for all versions.
I have one button (back button) on a view controller. Simple so far.
I have 2 view controllers with a table on each one.
if a user selects a row from either table it goes to the view controller with the back button on.
The back button needs to go back to the original view controller that the user was on when they selected the row.
I was considering unwind segues for this which is fine, but I cannot add two segues to one button. One for return to one table view and a return for the other table view dependent on which table view they used to access the view controller.
Any ideas ?
As Volk explained,
-(IBAction)devDismiss
{
NSLog(#" ------- dev dismiss .....");
// for a custom segue unwind, you must do this...
[self performSegueWithIdentifier:#"specialWord" sender:self];
// "specialWord" is the identifier set on storyboard for the
// custom unwind segue
/* for a "default" unwind segue, do this...
[self dismissViewControllerAnimated:YES completion:nil];
Note, if you used a push segue, you should use
[self.navigationController popViewControllerAnimated:YES]
If you used a modal segue, you should use
[self dismissViewControllerAnimated:YES completion:nil] "
*/
}
Note that indeed, you must also use "specialWord" in your segueForUnwindingToViewController: override, which will be in the DESTINATION (that is to say the ORIGINAL) view controller, the one underneath, that is to say.
-(UIStoryboardSegue *)segueForUnwindingToViewController:
(UIViewController *)toViewController
fromViewController:(UIViewController *)fromViewController
identifier:(NSString *)identifier
{
NSLog(#"we're in _your class name_, segueForUnwindingToViewController %#",
identifier);
// for some unwinds, we have a custom unwind we want to use.
// so, check the identifier:
if ([identifier isEqualToString:#"specialWord"])
{
YourCustomUnwindSegue *sg = [[YourCustomUnwindSegue alloc]
initWithIdentifier:identifier
source:fromViewController
destination:toViewController];
return sg;
}
// don't forget the break-away "return" inside any macthes.
// NSLog(#"note, if this message appears, it's likely you have a typo
// somewhere for 'specialWord' - unless you genuinely have a situation
// where it will also fall through to the 'default' unwind segue :O ");
// BE SURE TO return the default unwind segue otherwise
return [super segueForUnwindingToViewController:toViewController
fromViewController:fromViewController
identifier:identifier];
}
I have created a custom segue that presents a view controller inside a container that is very similar with Apple's own modal view controllers (I've implemented it as a UIViewController subclass).
I'm now trying to create a custom unwind segue but there's no way I can get the method -segueForUnwindingToViewController: fromViewController: identifier: to be called.
I've also implemented -viewControllerForUnwindSegueAction: fromViewController: withSender: on my container so I can point to the correct view controller (the one that presented this modal) but then the method that should be asked for my custom unwind segue doesn't get called anywhere.
Right now, the only way for me to dismiss this modal is to do it on the -returned: method.
Did anyone could successfully do this with a custom unwind segue?
EDIT:
A little bit more code and context
My unwind view controller is configured in the storyboard, not programatically.
I have these pieces of code related to the unwind segues in my controllers:
PresenterViewController.m
I'm using a custom method to dismiss my custom modals here (-dismissModalViewControllerWithCompletionBlock:).
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
fromViewController:(UIViewController *)fromViewController
identifier:(NSString *)identifier {
return [[MyModalUnwindSegue alloc] initWithIdentifier:identifier
source:fromViewController
destination:toViewController];
}
-(IBAction)returned:(UIStoryboardSegue *)segue {
if ([segue.identifier isEqualToString:#"InfoUnwindSegue"]) {
[self dismissModalViewControllerWithCompletionBlock:^{}];
}
}
MyModalViewController.m
Here I only use -viewControllerForUnwindSegueAction: fromViewController: withSender: to point to the view controller that I should be unwind to.
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action
fromViewController:(UIViewController *)fromViewController
withSender:(id)sender {
return self.myPresentingViewController;
}
The behavior I was expecting was that MyModalViewController was called to point to the view controller that should handle the unwinding and then this view controller had his -segueForUnwindingToViewController: fromViewController: identifier: method called before -returned: gets called.
Right now -segueForUnwindingToViewController: fromViewController: identifier: never gets called.
I must also say that I already tried different configurations. Everywhere I put my method to return the unwind segue it never gets called. I've read that I can subclass a navigation controller and then it gets called but I don't know how it would fit in my solution.
EDIT 2: Additional info
I've checked that MyModalViewController has his -segueForUnwindingToViewController: fromViewController: identifier: method called when I want to dismiss a regular modal view controller presented by it. This may be because he's the top most UIViewController in the hierarchy.
After checking this I've subclassed UINavigationController and used this subclass instead to contain my PresenterViewController. I was quite surprised to notice that his -segueForUnwindingToViewController: fromViewController: identifier: method is called as well.
I believe that only view controllers that serve as containers have this method called. That's something that makes little sense for me as they are not the only ones presenting other view controllers, their children are also doing so.
It's not OK for me to create logic in this subclass to choose which segue class to use as this class has no knowledge of what their children did.
Apple forums are down for the moment so no way to get their support right now. If anyone has any more info on how this works please help! I guess the lack of documentation for this is a good indicator of how unstable this still is.
To add to the answer from #Jeremy, I got unwinding from a modal UIViewController to a UIViewController contained within a UINavigationController to work properly (I.e how I expected it to) using the following within my UINavigationController subclass.
// Pass to the top-most UIViewController on the stack.
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)
toViewController fromViewController:(UIViewController *)
fromViewController identifier:(NSString *)identifier {
UIViewController *controller = self.topViewController;
return [controller segueForUnwindingToViewController:toViewController
fromViewController:fromViewController
identifier:identifier];
}
.. and then implementing the segueForUnwindingToViewController as usual in the actual ViewController inside the UINavigationController.
This method should be declared on the parent controller. So if you're using a Navigation Controller with a custom segue, subclass UINavigationController and define this method on it. If you would rather define it on one of the UINavigationController's child views, you can override canPerformUnwindSegueAction:fromViewController:withSender on the UINavigationController to have it search the children for a handler.
If you're using an embedded view (container view), then define it on the parent view controller.
See the last 10 minutes of WWDC 2012 Session 407 - Adopting Storyboards in Your App to understand why this works!
If you're using a UINavigationController and your segue is calling pushViewController then in order to use a custom unwind segue you'll need to subclass UINavigationController and override - (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier.
Say I have a custom unwind segue called CloseDoorSegue. My UINavigationController subclass implementation might look something like:
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
UIStoryboardSegue* theSegue;
if ([identifier isEqualToString:#"CloseDoor"]) {
theSegue = [CloseBookSegue segueWithIdentifier:identifier source:fromViewController destination:toViewController performHandler:^(void){}];
} else {
theSegue = [super segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier];
}
return theSegue;
}
Set your UINavigationController subclass as the navigation controller class in the storyboard. You should be good to go provided you have setup the Exit event correctly with "CloseDoor" as the identifier. Also be sure to call 'popViewControllerAnimated' in your unwind segue instead of dismiss to keep in line with UINavigationControllers push/pop mechanism.
iOS development Library
There is a discussion on iOS development Library along with this method.
- segueForUnwindingToViewController:fromViewController:identifier:
Make sure your MyModalViewController is the container role rather than a subcontroller of a container. If there is something like [anotherVC addChildViewController:myModalViewController];,you should put the segueForUnwindingToViewController method in some kind of "AnotherVC.m" file.
Discussion
If you implement a custom container view controller that
also uses segue unwinding, you must override this method. Your method
implementation should instantiate and return a custom segue object
that performs whatever animation and other steps that are necessary to
unwind the view controllers.
I noticed some strange behavior when passing data to a popover in iOS 5. The Popovers viewDidLoad method is called before prepareForSegue is called:
In Storyboard a segue connects a button of FirstViewController to PopoverViewController, which is embedded in a Navigation Controller.
For testing the two methods are logged:
/* FirstViewController.m */
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showPopover"]) {
NSLog(#"FirstViewController: prepareForSegue");
UINavigationController *navigationController = segue.destinationViewController;
PopoverViewController *popoverVC = (PopoverViewController *)navigationController.topViewController;
popoverVC.myProperty = #"Data to be passed";
}
}
and in the other ViewController:
/* PopoverViewController.m */
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"PopoverViewController: viewDidLoad");
}
Under iOS 6 the behavior is as expected:
2013-02-25 09:03:53.747 FirstViewController: prepareForSegue
2013-02-25 09:03:53.751 PopoverViewController: viewDidLoad
Under iOS 5 viewDidLoad of the PopoverViewController is called before prepareForSegue:
2013-02-25 09:05:28.723 PopoverViewController: viewDidLoad
2013-02-25 09:05:28.726 FirstViewController: prepareForSegue
This is strange and makes it hard to pass data to the Popover which can be used in viewDidLoad. Is there any solution to this?
I solved the problem now using the viewWillAppear: method instead of viewDidLoad. I think this is the better method for configuring views anyway (as the view could be already loaded and the view should be configured on every appear).
The viewWillAppear: method is loaded after the prepareForSegue in iOS 5 and iOS 6.
However, for those needing viewDidLoad the solution suggested by tkanzakic is the one that works then.
create a custom setter for your property and perform the operations you need from there, I use to do it in this way:
- (void)setCity:(GLQCity *)city
{
if (_city != city) {
_city = city;
[self configureView];
}
}
- (void)configureView
{
if (self.city) {
...
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
...
[self configureView];
}
I don't know anything about the differences between iOS 5 and 6, but I had run into similar confusions in the past. If you follow the general rules of thumb:
prepareForSegue is called before destination VC's viewDidLoad
viewDidLoad is called only after ALL outlets are loaded
Therefore, do not attempt to reference any destination VC's outlets in source VC's prepareForSegue.
Then, you would naturally arrived at either solutions - implement viewDidAppear vs. viewDidLoad, or set destination VC's properties only vs. touching any of its outlets.
Another lesson learned with regards to prepareForSegue is to avoid redundant processing. For example, if you already segue a tableView cell to a VC via storyboard, you could run into similar race condition if you attempt to process BOTH tableView:didSelectRowAtIndexPath and prepareForSegue. One can avoid such by either utilizing manual segue or forgo any processing at didSelectRowAtIndexPath.