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

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.

Related

Modally presented view controller can't unwind

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.

Adding a UIViewController to a UIView

I have ViewcontrollerA , which is a view made in storyboard.
In this view, i have a UIView that i have added in storyboard and create an outlet to it called containerView.
I wants to add some other viewControllerB (also made in storyboard) to the container.
Tried that :
//add to container a new view from storyboard,with id called serviceView
UIViewController *sv = [self.storyboard instantiateViewControllerWithIdentifier:#"ServiceView"];
[self.containerView addSubview:sv.view];
and got a crash.
How can i do that ?
Thanks .
It most likely crashed because sv is nil, though you should have posted what reason it gave for the crash in your question. If this is the reason, it's probably because your storyboard doesn't contain a view controller with that storyboard identifier. But even if you fixed this, this isn't the proper way of doing view controller containment.
As for adding a view controller to another view controller, you need to use the view controller containment API added in iOS 5. In fact you can even use the storyboard to do view controller containment without writing code. From the Object Library, you can drag out a "Container View". It will let you attach another view controller from your storyboard graphically.

UIKit messages deallocated object during segue on iOS 6

After upgrading xcode and compiling my app using iOS 6 SDK I got many crashes among the app. From what I was able to track down it looks like UIKit messages deallocated view controller during modal segue instead of the newly created one. Here's how it looks:
I have a Tabbar Controller which displays a Navigation Controller. Another view controller that is being presented by the Navigation Controller displays a Modal View Controller.
TabbarController --> NavigationController --> ViewController (presenting) --
| shows using modal segue |
--> ViewController (presented)
Steps to cause the crash:
Access the View Controller (presenting) in hierarchy shown above. It is not root view cntrl but higher.
Trigger the segue to the modal view controller.
Pick a tab from the tab bar (whichever) and go back to the same View Controller (presenting). Picking a tab calls popToRoot on the Navigation Controller.
Again trigger the modal segue to the View Controller (presented)
Crash: Zombie object - View Controller (presented) - got messaged
Why?
It looks like on previous iOS when popToRoot was called and View Controller (presenting) was being cleaned up also the modal view was destroyed. So when it was accessed again it was recreated and presented.
On iOS 6 from what Allocations Instrumentation shows the modal view is destroyed together with
View Controller (presenting). But when it's being accessed for some reason UIKit creates a new modav view controller but then messages the old one that is not existent anymore. Doesn't make sense.
One other thing that makes me wonder is that on iOS 5 Allocations Instrumentation tool never shows me the View Controller (presented) with retain count = 0 but iOS 6 does (afterwards it makes it -1).
I know this is probably very difficult question to help me with but maybe someone was already tackling problems with iOS 6 and such segues?
From Allocations Instrumentation tool I can tell that many things got changed in the implementation of segues on iOS 6.
I've end up implementing custom segues for presenting those modal views. Seems like a quite good idea here.
Maybe one is not supposed to display a modal view inside tab bar view?

Subview vs childViewController

This is a general question but I'll ask it using a specific example to avoid confusion:
I'm developing an app that has a UIPickerView among other things on a single screen. I was wondering if it's appropriate the make a custom PickerViewController class to control my UIPickerView and then add my PickerViewController as a childVC...
OR should I just create an instance of UIPickerView in my existing VC and control it from there?
When should I do either of these options and what are the advantages of them?
Appreciate the help amigos.
If the UIPickerView is simoultaneously on the screen along with other UI elements, then you should just add it as a subview. The concept of a view controller is for when you start managing a completely different UI/window/set of elements (however you call it). If the picker view closely belongs to the functionality of the other elements, you should not create a separate view controller for it.
iOS 5.0 introduced the notion of child view controllers. Originally, any view controller except for UINavigationController and UITabBarController had to be modal and full screen. In iOS 5.0, you can now have several UIViewControllers present on one screen. This means that one UIViewController can have many children (UIViewController), where each child is responsible for controlling more specific views and models.

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.