views:

318

answers:

3

I've been trying to figure out what happens in the view hierarchy when methods like pushViewController:animated, presentModalViewController:animated, and tab switches in UITabBarViewController are used, as well as UIAlertView and UIActionSheet.

(Side note: I'm doing this because I need to know whether a specific UIView of my creation is visible on screen when I do not know anything about how it or its superview may have been added to the view hierarchy. If someone knows a good way to determine this, I'd welcome the knowledge.)

To figure it out, I've been logging out the hierarchy of [[UIApplication sharedApplication] keyWindow] subviews in different circumstances. Is the following correct:

  1. When a new viewController is pushed onto the stack of a UINavigationController, the old viewController's view is no longer in the view hierarchy. That is, only the top view controller's view is a subview of UINavigationController's view (according to logs, it's actually several private classes such as UILayoutContainerView). Are the views of view controllers below the top controller of the stack actually removed from the window?

  2. A very similar thing happens when a new viewController is presented via presentModalViewController:animated. The new viewController's view is the only subview of the kew window. Is this correct?

  3. The easiest thing to understand: a UIAlertView creates its own window and makes it key.

  4. The strangest thing I encountered: a UIActionSheet is shown via showInView: method, the actionSheet isn't in the view hierarchy at all. It's not a subview of the view passed as an argument to showInView:, it isn't added as a subview of the key window, and it doesn't create its own window. How does it appear, then?

  5. I haven't tried this yet, so I'd like to know what happens in the keyWindow hierarchy when tabs in a UITabBarController are switched. Is the view of the selected UIViewController moved to the top, or does it work like with pushViewController:animated and presentModalViewController:animated, where only the displayed view is in the window hierarchy?

A: 

I now performed the same sort of logging for UITabBarViewController, so, considering part 5:

It seems to work the same way as UINavigationController: only the view of the view controller associated with the currently active tab is in the view hierarchy of keyWindow.

executor21
+1  A: 

I think you're conceptually confusing views and views controllers. Both the navigation controller and the tabbar controller manager other view controllers. Neither manages views.

There is no view hierarchy for an entire app, there is only a hierarchy of view controllers. A view hierarchy only exist when each controlled view is loaded. Then you have a temporary hierarchy of window-->viewController.view-->viewController.view.subviews. When you push/pop another view controller on the stack, you get another view hierarchy.

The view hierarchy is an stage illusion that the user sees. The view controller hierarchy is the stage-set/backstage that the programer uses to create that illusion. Don't confuse the two.

So:

  1. Are the views of view controllers below the top controller of the stack actually removed from the window? Yes. When the view controller is popped off the stack its view disappears. Why waste memory holding a view that the user may never see again?
  2. The new viewController's view is the only subview of the kew window. Is this correct? Yes.
  3. ...a UIAlertView creates its own window and makes it key. Yes.
  4. ...the actionSheet isn't in the view hierarchy at all. ... How does it appear, then? It's added by the application above the window. That's what makes it special. It's also what makes is able to appear above all other views even when those views fail.
  5. Is the view of the selected UIViewController moved to the top Yes. Again, stage illusion. You swap out the one controllers view for another controller's view.
TechZen
I think you misunderstood my question somewhat. By view hierarchy, I meant the hierarchy of views and subviews **as they appear on the screen**. My question concerning the navigation controller was how the views of view controllers on its stack are added to that screen hierarchy.I wasn't talking about the view controller that was popped. I was talking about the view controller over which another view controller has been pushed. It's still in the nav controller's stack, just not on top. I wanted to confirm that I got things right: only the top view controller's view is actually in the window
executor21
(Ran out of space in the previous comment)Thanks for the info on the action sheet.By swap, you mean only the visible view is in the window?
executor21
(1) Yes, one view is swapped for another. There is only one viewController.view active at any one time. (2) Alerts and UISheets are technically sibling views of the window i.e. they exist at the same top level of the hierarchy. As a result IIRC, you won't see them (alerts at least) in any dump of a window's subviews.
TechZen
Thanks, but the logs I saw show that the UIAlertView is a subview of its own window (of class _UIAlertOverlayWindow), which is the key window when the alertView is on screen, while UIActionSheet is not in any window at all -- the key window when it is displayed is still the main app window, and the actionSheet is **not** its subview.
executor21
TechZen has it almost perfectly backwards here. There is indeed a view hierarchy for the entire app, starting with one (or sometimes more) UIWindow and descending into its nested subviews. Some of these subviews can have view controllers associated with them that get to participate in the responder chain, but this is not strictly necessary. You can write an app that has views but no view controllers. The nav and tab bar controllers do indeed manage views. They respond to user actions by manipulating the view hierarchy, adding and removing views for the controllers they manage.
cduhn
(continued) It's true that a view disappears when its controller is popped off the stack. It disappears because the UINavigationController removes it from the view hierarchy. The view hierarchy isn't something temporary that gets recreated each time you push or pop a controller. The view hierarchy is born when you add the first subview to your UIWindow, typically in applicationDidFinishLaunching:. It ends when the app exits.
cduhn
(continued) When we look at an app's UI, we're looking at a tree of CALayers, each of which typically maps to a UIView in a one-to-one delegate relationship. The UIView hierarchy mirrors the CALayer hierarchy. The hierarchical relationship between view controllers is much more abstract since you can never actually see a view controller. The nav stack, the tab bar controller's viewControllers array, and the hierarchical relationship between modal view controllers and their parents all exist simply to tell the controllers which views to add and remove when users navigate between screens.
cduhn
@cdun -- Everything you said is true but irrelevant to the point under discussion. In a well designed app, you ignore the behind the scenes view hierarchy because the way the app draws the views is irrelevant to the logical relationship between the views. Each view of a view controller is completely standalone in respect to other views. You should be able to load them in any order and they will still work as long as the data they represent exist in the data model. The only critical hierarchy is the view controller hierarchy. Developers can and should ignore the app level view hierarchy.
TechZen
I would add that you can only "You can write an app that has views but no view controllers." by breaking MVC and cramming all your controller logic into a view. The language and API allows this but it is bad, bad design. It shatters encapsulation and ties every view into every other view which causes a minor change anywhere to break everything. Its the modern OOP equivalent to using goto statements instead of functions. If you see the views as the logical heart of the app and the view controllers and data model as secondary, you're in for a world of hurt down the line.
TechZen
@TechZen - I certainly agree that putting controller logic into your views would be a bad design decision. I also agree that for most basic apps developers don't need to worry about the view hierarchy manipulations that UIKit executes on our behalf. Still, I share the OP's curiosity about what's going on behind the scenes, and have found it helpful and sometimes necessary to know how these things work. Our own views must coexist within the same hierarchy as private UIKit views, and Apple doesn't always isolate us from the side-effects of its private implementation.
cduhn
A: 

the following methods on UIView may do what you need:

– didAddSubview:
– didMoveToSuperview
– didMoveToWindow
– willMoveToSuperview:
– willMoveToWindow:
– willRemoveSubview:
Remover