How to inspect subviews hierarchy of any UIView

In this programming tutorial (or guide, if you want so), I am going to explain a simple way of printing out the entire subviews hierarchy of any UIView (subclass). We are going to accomplish this by creating a Category on UIView, and adding a single recursive method which will do its job of going down through the entire tree-structure of the view.

Now for those of you who are wondering what a ‘category’ is: It’s a neat Obj-C way of adding methods to classes without any need to subclass them. For more details see the Apple’s documentation.

There are several cases when you are in need of finding out some underlaying stuff, for example what the MKMapView consist of; in order to modify certain labels, etc. I think there is really no need to talk about it more.

Here is what the header of our category (file UIView+printSubviews.h) looks like:

 
#import <Foundation/Foundation.h>
 
 
@interface UIView (PrintSubviews)
 
- (void)printSubviewsWithIndentation:(int)indentation;
 
@end

The way of declaring a Category in Objective-C is just great and simple, isn’t it? What’s even better is that our method will work with any subclass of UIView (UIScrollView, MKMapView, UITableView, UIButton – to name a few). Now here comes the actual implementation file (UIView+printSubviews.m):

#import "UIView+printSubviews.h"
 
 
@implementation UIView (PrintSubviews)
 
- (void)printSubviewsWithIndentation:(int)indentation {
 
    // Get all the subviews of the current view
    NSArray *subviews = [self subviews];   
 
    // Loop through the whole subviews array. We are using the plain-old C-like for loop,
    // just for its simplicity and also to be provided with the iteration number
    for (int i = 0; i < [subviews count]; i++) {
 
        // Get the subview at current index
        UIView *currentSubview = [subviews objectAtIndex:i];   
 
        // We will create our description using this mutable string
        NSMutableString *currentViewDescription = [[NSMutableString alloc] init];
 
        // Indent the actual description to provide visual clue of  how deeply is the current view nested
        for (int j = 0; j <= indentation; j++) {
            [currentViewDescription appendString:@"   "];
        }
 
        // Construct the actual description string. Note that we are using just index of the current view
        // and name of its class, but it's up to you to print anything you are interested in
        // (for example the frame property using the NSStringFromCGRect(currentSubview.frame) )
        [currentViewDescription appendFormat:@"[%d]: class: '%@'", i, NSStringFromClass([currentSubview class])];
 
        // Log the description string to the console
        NSLog(@"%@", currentViewDescription);
 
        // Be good memory citizen
        [currentViewDescription release];
 
        // the 'recursiveness' nature of this method. Call it on the current subview, with greater indentation
        [currentSubview printSubviewsWithIndentation:indentation+1];
    }
}
 
@end

I guess there is nothing more to explain, really. It’s that simple. Now I will show you an example of usage, along with the output that this method produces. I will call it on an instance of MKMapView with some annotations, one of them selected (therefore displaying the callout view).

Just a note for the less experienced of you: You need to import the header file of this category in each class where you are going to use it, like this:

#import "UIView+printSubviews.h"

then call the -printSubviewsWithIndentation: method on the view you wish to inspect:

NSLog(@"*** Printing out all the subviews of MKMapView ***");
[mapView printSubviewsWithIndentation:0];

and that’s what the output looks like (in my case):

Subviews-tree of MKMapView

Now you can see all the behind-the-scenes views that the mapView consists of. And the numbers in square brackets in front of every line are exactly what they suggest to be – indexes of the subviews in its superview’s subview array.

You can for example retrieve the MKAnnotationContainerView using the following statement:

UIView *annotationContainerView = [[[[[[mapView subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:1];

This is of course not very safe. It’s generally not a good idea to depend on the particular order of subviews. So you should always test if the [subviews count] > 0, and perhaps use the NSClassFromString() in a similar manner like this:

    for (UIView *subview in subviews) {
        if ([subview isKindOfClass:NSClassFromString(@"NameOfTheClass")]) {
            // We found the subview that we want
        }
    }

The last thing I wanted to do is to warn you: Apple discourages developers from doing this, because subviews of an UIKit class are considered to be private, and therefore they can change in future updates of the framework. So if you decide to modify or alter any of the subview, you should do it only when there is no other choice. And you should retrieve the view as defensively as possible, and be prepared for the scenarios when the code doesn’t get the view it wanted.

I hope this post was helpful to you guys. You are welcome to point out any weaknesses or suggest any improvements using the comment section below.

7 comments

  1. TypeSlalaypob says:

    Hello! Just want to say thank you for this interesting article! =) Peace, Joy.

  2. If subviews of uitableview, How can I access to subviews.

    Thanks

  3. Malcolm Hall says:

    There is a built in way to do this, try this magic:

    NSLog(@”%@”, [view recursiveDescription]);

    It even has a nicer indentation using lines.

  4. Ben Gotow says:

    Hi Lukas—thanks for posting! I like the bit about locating a subview by class name – that’s a great idea. I used to print the class hierarchy all the time, but I wrote an interactive view debugger called the Spark Inspector to avoid having to do it. If you find yourself printing the view hierarchy often you should try it out! You add a framework to your app and it shows your whole view stack and a 3D view of what it looks like. If you find it useful I’d love to send you a license key for it so you can explore it on your blog.

    • Lukas says:

      Hi Ben, thanks for your comment, the Spark Inspector looks really sweet! I will take a closer look at it, but not now, because I have to do quite a lot of things in this week, before heading to San Francisco for WWDC. As you probably noticed, my blog is mostly unkept for most of the last year and the number of daily visitors is (unsurprisingly) pretty low, so it wouldn’t do you much of publicity :P , but if you would be willing to provide the license key anyway, I would consider making a blog post about it. Or, if you are going to attend WWDC, we could meet there and talk in person, I would love to hear more. ;) .

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">