Ad

Our DNA is written in Swift
Jump

Visual View Debugging

In this article I want to summarize a couple of “barefoot techniques” for tuning your views. Sometimes you are painstakingly putting together a UI with multiple views and you cannot tell any more where one ends and another begins.

The debugger is not really working well on this because most of the interesting information about views is hidden behind properties and you cannot usefully inspect their current contents because properties are really methods the value of which is only set when you actually call them.

But I want to share some easy techniques that I started using so that I can get this visual puzzle untangled.

There are two kinds of approaches to debugging the visuals of a UIView. You can either look at some textual description of it and see if the numbers match up with what you’re expecting. Or you can mark and highlight a view such that you can visually judge whether it is in the right place.

Getting View Info

As any NSObject subclass UIView also has a descriptive description. It will tell you the view’s frame, class, autoresizing mask so you don’t have to fuss to extract the view’s frame with NSStringFromCGRect but you can simply NSLog the view directly.

NSLog(@"%@", view);

Output from this contains the current frame, autoresizing mask and info about the backing layer. Note: on iOS all views are layer backed and you can choose your own layer class to be used for any view. But if you don’t then CALayer is used.

<UIView: 0x4b15960; frame = (0 20; 320 460); autoresize = W+H; layer = <CALayer: 0x4b15a40>>

This simple technique can also reveal if you might inadvertently positioned the origin of the view on a non-integer value. This typically causes text to look blurry due to the antialiasing that get’s applied there. Here’s an example from one of my recent projects, you’ll have to open up the image in full resulution to see the difference.

Something like this might happen of you don’t set a view’s frame, but instead set it’s size via the bounds or center property. If the width or height are not dividable by 2 then you’ll get this affect, but you’ll only notice it when some perfectionist asks you why some label does not look as crisp as all the others.

NSLogging a simple view goes a long way, but it cannot reveal the hierarchy of views. One might be tempted to write some recursive category extension to also show a view’s subviews. But actually this work has already been done for us, by Apple!

UIView has a hidden (i.e. private) method named recursiveDescription which works just like description but recursively walks through a view’s children and their children and shows it in sort of a textual tree. You cannot have this in production code, but just for debugging all you need is to add a few lines to tell the compiler that this is a valid method.

@interface UIView (debug)
 
- (NSString *)recursiveDescription;
 
@end

Alternatively you can simply ignore the warning that the method might not exist. Output from this might look like:

<UIWindow: 0x4e31810; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x4e319a0>>
| <UIView: 0x6015f60; frame = (0 20; 320 460); autoresize = W+H; layer = <CALayer: 0x6016040>>

You see a pipe symbol that shows which superview a view belongs to. You also see that the individual views are just shown with their regular description.  See a view’s structure of children might tell you some interesting things about stock views that come with the SDK. For example you would see that UIScrollView has some small UIImageViews as children: those are the scroll indicators!

Don’t forget to remove the NSLog statements you added just for debugging. You don’t have to remove all log statements for production code, but you can leave in log statements that give some interesting information. But if you leave in logs of view descriptions you deserve to be flogged.

Seeing the Dimensions of the View

If you have a view visible but are uncertain if all of it is showing then you can give it’s layer a distinct border color to visualize it. If you see the border all around and nothing is cut off then all is well.

By linking in the QuartzCore.framework and adding the <QuartzCore/QuartzCore.h> at the top of your file you gain access the properties of the UIView’s backing layer. Without that you can only get as far as the view’s layer, but all the properties of the CALayer class are defined in this header.

This allows you to set a border so you can see if your view is fully visible. This example sets it to red (note the use of CGColor instead of UIColor) and the border width to 1 so that it’s visible. I recently used this to see if a video preview layer is indeed showing fully.

view.layer.borderColor = [UIColor redColor].CGColor;
view.layer.borderWidth = 1.0;

Another technique that I love to use, especially when working with multi-page scrollviews is to have the child views and the parent views all with different background colors. Now you could just set the backgroundColor for all but I found that it gets boring after a while because you always think of the same primary colors. Red. Green. Blue. Yellow. Purple if you feel fancy. Wouldn’t it be much nicer if you saw a fresh random color every time you start the app again to see if the views line up?

A simple category on UIColor gives you a randomColor method so that you never run out of ideas for temporary background colors.

// this in the header
@interface UIColor (debug)
+ (UIColor *)randomColor;
@end
 
// this in the implementation
@implementation UIColor (debug)
+ (UIColor *)randomColor
{
    CGFloat red = (arc4random()%256)/256.0;
    CGFloat green = (arc4random()%256)/256.0;
    CGFloat blue = (arc4random()%256)/256.0;
 
    return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}
@end

Never again those boring primary colors!

Conclusion

These are the two methods of how to get useful descriptions of views as well as how to visually mark views so that you can be certain that they are in the right spot. If you know any others that are also fun, then please comment.


Categories: Recipes

4 Comments »

  1. Your post inspired me to open source a tool I’ve been using internally for a little while now:

    https://github.com/domesticcatsoftware/DCIntrospect

    It’s a small library that you load up on app launch and is designed for use in the simulator. From there you start it up by pushing the spacebar anywhere in the app, and you can click on views to see their frame and use shortcut keys to do other useful functions. See the page for more info.

  2. Great post! We used to do this sort of thing all the time at Foundry376, so we developed an app called the Spark Inspector (http://www.sparkinspector.com/) to make things easier. It’s sort of like Instruments for your UIViews—you can explore your views while your app is running, change things to see what you need to do to fix your code, etc.. I’d love to get your thoughts on it. It’s shareware but I’d be happy to send you a free copy if you want to post about it on Cocoanetics.