Uncaught exception ‘UIViewControllerHierarchyInconsistency’, reason: ‘child view controller: should have parent view controller: but actual parent is:’ OR Apple’s new UIViewController Hierarchy

Since iOS 5 Apple has added a fantastic new feature: a fully re-engineered UIViewController hierarchy concept.

What it allows you to do is to pretty much patch together a screen from several UIViewControllers.
I remember a guy at my former workplace who kept complaining: why do I always have to have one ‘big’ UIViewController to manage the entire ‘screen’? I wish I could define different areas in my ‘screen’, each of them being handled by its own UIViewController.
This is exactly what he ended up programming himself because it made his project a lot more flexible.

With iOS 5 Apple provides us with exactly this feature. You have one ‘big’ UIViewController to whose view you add contents from other ‘tiny’ UIViewControllers views. The contents in these view areas are managed independently by the corresponding tiny view controller instead of the parent one.

The two methods allowing you to do this are “addChildViewController” (to add a child obviously, called on the parent view controller) and “removeFromParentViewController” (which removes the child from its parent view controller).
In conclusion, it pretty much works like the adding and removing of subviews to a view.

GREAT FEATURE, I say!

With this change, another one came. We used to have [controller parentViewController] returning *either* the root view controller of the view hierarchy (e.g. a navigation controller) *or* the view controller that presented the new view controller via “presentModalViewController”.

In iOS 5 “parentViewController” ONLY returns the root view controller. In case you don’t use UINavigationControllers or UITabBarControllers it will return nil.
Additionally, a new method called “presentingViewController” has been created, which should be used whenever “presentModalViewController” has been called, in order to determine the parent view controller.

Now, this sounds all fantastic at first sight. If you hop on iOS 5 and start developing your app from scratch things are great.
If you had an older version running iOS 4.x and want to make an upgrade… you might spend many hours (or days!?) cursing Apple.

Why?
Because you eventually will either wonder why your app is hanging (parentViewController returns nil… and sending messages to nil doesn’t cause crashes in Objective-C)…

or…

you will fight this great exception:
“Uncaught exception ‘UIViewControllerHierarchyInconsistency’, reason: ‘child view controller: should have parent view controller: but actual parent is:”.

I wanted to do an upgrade on my FixVegas app, which uses code from my blogpost on dashboard animations as seen in the old facebook and Google iPhone apps.
There I push views onto a UINavigationController but with custom animations (scale in, scale out, etc.).

I got the exception from above and kept scratching my head what exactly causes it. I thought maybe I use “parentViewController” somewhere… but I wasn’t using it.

Until I finally figured it.

The app was crashing at lines where the view of a view controller that had previously been added to a hierarchy was being added as a subview to the view of the view controller to be pushed next:

<code> [self.topViewController.view addSubview:controllerToPush.view]; </code>

 

What I then figured was that by calling “addSubview”, according to the new iOS hierarchy, I was adding controllerToPush as a child view controller to self.topViewController.
But that was conflicting with the fact that it already HAD been added as a child view controller of my UINavigationController once it had been pushed.

So, the solution was ├╝bersimple (who would have thought?!):
Before adding the view as subview add the line:

<code> [controllerToPush removeFromParentViewController]; </code>

 

and all your problems are solved!

Bottom line is:
Adding a UIViewController’s view as subview to another UIViewController’s view causes the first to become a child of the second. This might conflict with UINavigationControllers or UITabBarControllers that are used in the app.