Modally presented view controller can't unwind - xcode6

I have a UIViewController sublcass (VC1) embedded in a UINavigationController. VC1 triggers a modal segue to another UIViewController subclass (VC2) which is embedded in its own, different UINavigationController. Inside of an action method triggered by a UIBarButtonItem in VC2's nav bar, I call
[self performSegueWithIdentifier:#"SomeString" sender:nil]
which corresponds to an unwind method inside VC1. For some reason, the transition does not occur.
It only became a problem after switching to XCode 6. It worked fine in XCode 5. Any ideas?

This issue has been doing the rounds and I have the exact same problem. Unfortunately there is no good solution to it yet, other than go back to the old delegate pattern.
If you subclass your parent view controller navigation controller and implement - (UIViewController*)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender you will see that the modal is actually looking for your unwindSegue method on the navigation controller instead of the view controller that presented the modal.
The problem gets even more amplified if you have a container view controller as the method above gets called all the way up the controller chain to the storyboard's initial view controller.
There's a potential workaround here Unwind Segue not working in iOS 8 but it has its downsides and side effects as well.

Related

Using Storyboard ViewControllers

What would be main difference between using instantiateViewControllerWithIdentifier and performSegueWithIdentifier ?
I think that performSegue.. is used for normal pushing of viewControllers as instantiateViewController is used for some kind of modal showing of viewController and then dismissing it when used.
Since there are also modal and custom push in storyboard I'm not sure about my theory so if anyone could explain when to use which one ?
Thanks.
The difference is that performSegueWithIdentifier is used to transition to a specific view controller that is connected by a segue in interface builder (Transition1 in my screenshot).
instantiateViewControllerWithIdentifier can be used to instantiate any view controller on a storyboard, regardless if it's connected by a segue or not (Transition2 in my screenshot).
Push, modal, or any other custom transition could be used for either scenario.
instantiateViewControllerWithIdentifier can also be used to instantiate view controllers from separate storyboard files. A segue has to be within the same storyboard file.
The segue identifier used in performSegueWithIdentifier needs to be set in interface builder.
The view controller identifier used in instantiateViewControllerWithIdentifier is the Storyboard ID field in interface builder.

How do IOS know which viewController is being viewed and hence need viewWillAppear to be called

How does iOs know?
Does each view has a pointer to it's controller?
What happened?
When we pop a viewController from navigation, does the navigationController arrange which view should be called?
For example:
If I added:
[[BNUtilitiesQuick window] addSubview:[BNUtilitiesQuick searchController].view];
viewWillAppear will be called.
ios does know which viewControler viewwillappear should be called even under cases in the question. There is no way I can think of how they know that without a pointer from view to viewcontroller.
However, window doesn't know the viewController. I am passing the view outlet of the controller not the controller. How can iOs 5 knows that it has to call [[BNUtilitiesQuick searchController] viewWillAppear:YES]
Navigation Controller maintains a stack of view controllers.
Once a view controller is popped, it is removed from the stack, and now the view controller exactly below this is the first view controller.This is how it works.
You can check documentation for more details - http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007457
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html#//apple_ref/doc/uid/TP40011313-CH2

Multiple Presented View Controllers and Rotation on iPad

I have recently run into an issue when porting some code from an iPhone app over to the iPad. It may be related to the issue described here, though I found the explanations/solutions of that question unsatisfactory. The scenario is this:
View controller 'A' (The Root View Controller) presents a modal view controller (call it 'B') with the "Form Sheet" modal presentation style.
View controller B presents view controller 'C' with the "Full Screen" modal presentation style.
The iPad is rotated while view controller C is the top-most presented view controller.
Upon dismissal of C, B is re-displayed, but has the incorrect orientation.
As far as I can tell, there should not be an issue with chaining of multiple presented view controllers -- in fact, this behavior is explicitly supported in the Presenting View Controllers from Other View Controllers documentation. I have also read the following statement in the iOS 5 Release Notes:
Rotation callbacks in iOS 5 are not applied to view controllers that are presented over a full screen. What this means is that if your code presents a view controller over another view controller, and then the user subsequently rotates the device to a different orientation, upon dismissal, the underlying controller (i.e. presenting controller) will not receive any rotation callbacks. Note however that the presenting controller will receive a viewWillLayoutSubviews call when it is redisplayed, and the interfaceOrientation property can be queried from this method and used to lay out the controller correctly.
As far as I can tell, this does not occur -- View controller B receives a call to -shouldAutoRotateToInterfaceOrientation but the value the interfaceOrientation parameter in this call is the value of view controller B's interface orientation when it presented view controller C, not the value of C's interface orientation upon dismissal. Since we're on an iPad, all these view controllers return YES in -shouldAutoRotateToInterfaceOrientation. Thus the bounds of B's view never change, so -willLayoutSubviews is never called.
I have tried saving the orientation of view controller C in a callback to B before B dismisses it, and then using that information the next time -shouldAutoRotateToInterfaceOrientation is called and returning YES only for the orientation of C when it is dismissed. This fixes, the broken UI that appears without making this check, but view controller B does not update its interface orientation to this value, so subsequent modal presentations will animate in/out from the wrong side of the device.
Has anyone been able to successfully get a view controller configuration like this working? It doesn't seem like that unusual of a scenario, so I am a little surprised that it isn't working as I initially expected it to.
Thanks in advance.
In my opinion multiple chained modal view controllers result in a confusing and annoying user experience if you don't use a navigation controller. I think View controller B should be in a navigation controller (you don't have to show the nab bar if you don't want).
Modal presentation is really supposed to be for single dead-ended entities (a single view controller or a navigation controller containing multiple children view controllers).
Out of interest, are you saying that this works fine on iPhone but not on iPad? Or did you not allow rotation on the iPhone version?
I've also found this thread which says that presenting your modal view controllers from the root view controller may help.
I have worked on multiple modal view controllers being presented on iPhone. There is no problem with layout, unless there is something wrong with my own code for handling multiple orientations. Auto rotation methods actually never get called when view controller is behind another view controller, so I would also adjust layout on viewWillAppear: as well.
On viewWillAppear:, willRotateToInterfaceOrientation:duration:, and didRotateToInterfaceOrientation:, I would adjust the layout to the correct orientation as needed, for example:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self switchToInterfaceOrientation:self.interfaceOrientation];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:interfaceOrientation duration:duration];
[self switchToInterfaceOrientation:toInterfaceOrientation];
// be careful, self.view.frame is still according to self.interfaceOrientation
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// self.view.frame is updated
// update something else here
}
- (void)switchToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// update your layout here
}
I'm not sure about how the above code would behave on view controllers on iPad. Since it supports view controller with different modal presentation styles, there might be some surprises.

What methods are called when returning to UINavigationController's root view?

What methods are called when returning to a previous view, or the root view, of a UINavigationController?
Also, when the user taps the back button to return to the previous view, is everything in the view that's disappearing released, and its values unretrievable from the parent view?
viewWillDisappear:animated: will be called on a view controller before it is popped from the navigation stack.
This is the place to do anything that you need to do when the user goes "back" in the navigation controller. You can access any other controllers in the navigation stack via self.navigationController.viewControllers which is an array of all the view controllers currently in the stack, with the root view controller at index 0.
You can use the viewWillAppear, viewDidAppear, viewWillDisappear and viewDidDisappear in your view controllers. This works independently of UINavigationController.
UINavigationController also has two delegate method. Maybe they would help you:
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:

viewWillAppear and viewWillDisappear callbacks when a ViewController's view is hidden by another view & UIViewController Containment in iOS5

The UIViewController docs mentions about -viewWillDisappear:
"This method is called in response to a view being removed from its
window or covered by another view. This method is called before
the view is actually removed or covered and before any animations are
configured."
In iOS 4.3 and lower we are supposed to present a viewController and not add a viewController's view to the view hierarchy explicitly, so the calls -viewWillDisappear or -viewDidDisappear would be triggered when a new view controller is being presented over the existing view, in which case 'covered by another view' is true! But what if a viewController's view is hidden since another view obstructs the viewController's view? Do we get these callbacks?
Well, in iOS 5 there is a UIViewController containment concept where views can be directly added as subviews in view hierarchy by setting the parent-child relationship between viewControllers. So, unlike <= 4.3 OS, -viewWillDisappear and -viewDidDisappear calls should ideally be triggered when a viewController's view is obstructed or covered by some other view, which I have verified by a sample project that it is not happening in SDK 5.0.
Has anyone found this problem related to these callbacks?
Or, is my understanding correct?
Thanks,
Raj
Someone has the same kind of problem here :
iOS 5 : -viewWillAppear is not called after dismissing the modal in iPad
You should read the answers, I found them very interesting.