24

How do I determine what the view structure on my iPhone program while running on the simulator?

Chris
  • 419
  • 1
  • 3
  • 8

7 Answers7

60

There's a built-in way to dump the view hierarchy: recursiveDescription

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

It will output something like this:

<UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
   | <UIRoundedRectButton: 0x6a103e0; frame = (124 196; 72 37); opaque = NO; […]
   |    | <UIButtonLabel: 0x6a117b0; frame = (19 8; 34 21); text = 'Test'; […]

See: https://developer.apple.com/library/content/technotes/tn2239/_index.html

Cœur
  • 37,241
  • 25
  • 195
  • 267
marcprux
  • 9,845
  • 3
  • 55
  • 72
29

You don't have to write a single log statement or alter your code at all.

Hit the pause button.

enter image description here

Enter the following into the console

po [[UIWindow keyWindow] recursiveDescription]
Robert
  • 37,670
  • 37
  • 171
  • 213
  • 1
    As a shortcut, this is available with the `pviews` command in Facebook's [Chisel](https://github.com/facebook/chisel) debugging library. – bcattle Jan 07 '15 at 23:25
17

These answers are all variants of -recursiveDescription, but it's been about three years since the question was posted and these days there are much better ways to inspect your view hierarchy. -recursiveDescription was always a bit unmanageable with large hierarchies - you'd spend more time looking through your Output Log than you spent programming!

Here are some newer solutions:

  • You can use DCIntrospect to print out the view hierarchy and view properties, and get some cool onscreen debugging markers.

    DCIntrospect View

  • You can use the Spark Inspector to see your app's view hierarchy and interact with it in 3D. The Spark Inspector has a sidebar that mirrors Interface Builder and you can adjust your views while your app is running. (Full disclosure: I am the lead developer of this tool—send me your feature requests!)

    Spark Inspector Main Panel

  • You can use PonyDebugger to connect the Chrome Inspector to your app - you can view your app's view hierarchy in the DOM view, and they also capture network traffic, which can be useful.

    PonyDebugger Web View

Ben Gotow
  • 14,805
  • 3
  • 42
  • 47
10

Along the lines of what Yannick suggests, Erica Sadun has code here that pretty-prints the view hierarchy (with class and frame information). You can make this into a UIView category with the following interface:

#import <UIKit/UIKit.h>

@interface UIView (ExploreViews)

- (void)exploreViewAtLevel:(int)level;

@end

and implementation:

#import "UIView+ExploreViews.h"

void doLog(int level, id formatstring,...)
{
    int i;
    for (i = 0; i < level; i++) printf("    ");

    va_list arglist;
    if (formatstring)
    {
        va_start(arglist, formatstring);
        id outstring = [[NSString alloc] initWithFormat:formatstring arguments:arglist];
        fprintf(stderr, "%s\n", [outstring UTF8String]);
        va_end(arglist);
    }
}

@implementation UIView (ExploreViews)

- (void)exploreViewAtLevel:(int)level;
{
    doLog(level, @"%@", [[self class] description]);
    doLog(level, @"%@", NSStringFromCGRect([self frame]));
    for (UIView *subview in [self subviews])
        [subview exploreViewAtLevel:(level + 1)];
}

@end
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • This solution does what I asked for. The fun part is always trying to ask the right question. – Chris Feb 26 '10 at 21:24
  • I notice some extra subviews in view hierarchy between viewDidLoad and viewDidAppear. I am not adding any subview to my view programatically. How could it be happening when the entire view hierarchy is already there in the nib file? I guess in that case, both viewDidLoad and viewDidAppear should print the same view hierarchy but that's not what I observe. – SayeedHussain Mar 20 '15 at 06:02
5

Try this magic:

NSLog(@"%@", [self.view recursiveDescription]);
malhal
  • 26,330
  • 7
  • 115
  • 133
  • 2
    This worked, but I had to use `performSelector:withObject:` in Xcode 5.1.1; otherwise, it was an error. – johnnieb May 15 '14 at 16:23
3

you can use some algo like this:

-(void)inspectView:(UIView *)aView level:(NSString *)level {
    NSLog(@"Level:%@", level);
    NSLog(@"View:%@", aView);

    NSArray *arr = [aView subviews];
    for (int i=0;i<[arr count];i++) {
        [self inspectView:[arr objectAtIndex:i]
          level:[NSString stringWithFormat:@"%@/%d", level, i]];
    }
}

And add this line in an viewDidLoad for example:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self inspectView:self.viewControllers level:@""];
}

source: view hierarchy

Yannick Loriot
  • 7,107
  • 2
  • 33
  • 56
1

I've also dealt with this problem, and I came up with a single recursive method as a Category on UIView. It uses only basic Foundation classes, so it is not as complex as Erica's solution.

Here is the Category method:

- (void)printSubviewsWithIndentation:(int)indentation {

    NSArray *subviews = [self subviews];   

    for (int i = 0; i < [subviews count]; i++) {

        UIView *currentSubview = [subviews objectAtIndex:i];   

        NSMutableString *currentViewDescription = [[NSMutableString alloc] init];

        for (int j = 0; j <= indentation; j++) {
            [currentViewDescription appendString:@"   "];   // indent the description
        }

        // print whatever you want to know about the subview
        [currentViewDescription appendFormat:@"[%d]: class: '%@'", i, NSStringFromClass([currentSubview class])];

        // Log the description string to the console
        NSLog(@"%@", currentViewDescription);

        [currentViewDescription release];

        // the 'recursiveness' nature of this method
        [currentSubview printSubviewsWithIndentation:indentation+1];
    }
}

For more detailed information, check out my blog post about this issue: http://www.glimsoft.com/01/07/how-to-inspect-subviews-hierarchy-of-any-uiview/

Lukas Petr
  • 11
  • 1