View Debugging in Xcode 6

When you’re developing an app, sometimes there may be a bug with your views or auto layout constraints that isn’t easy to find just by looking through your code.

It pays to know the technique of view debugging – and this has never been easier with the advent of Xcode 6.

Instead of printing frames to the console and trying to visualize layouts in your head, you’re now able to inspect an entire view hierarchy visually – right from within Xcode. What an incredible time to be alive!

This tutorial will take you through all the different options that are at your disposal. So, are you ready to write some code? That’s too bad, because you won’t. :]

Instead, you’ll inspect the view hierarchy of an open source library to better understand how it was written — without even looking at any code.

Getting Started

The library you’ll use in this tutorial is JSQMessagesViewController, written by Jesse Squires. The UI for this library should look very familiar, as Jesse built it to look similar to the Messages app.

To get started, head over to the GitHub project page, download the source code, and unzip into a directory.

Note: The library uses CocoaPods to manage its dependencies on other libraries. If you’re unfamiliar with how CocoaPods works, please take a look at this CocoaPods tutorial here on the site before proceeding.

Next, navigate to the unzipped project directory in Terminal and run pod install to install the required dependencies. Then, open JSQMessages.xcworkspace and build and run the app on the iPhone 5s simulator. (You can use any simulator, but dimensions in this tutorial are based on a 4-inch display, so choosing the same will make it easier to follow.)

<div class="note"

Note: Live View Debugging is only available if you run your app on iOS 8. You cannot use this tool on iOS 7, even if you’re working in Xcode 6.

Tap on Push via storyboard and you’ll notice you’re now in a text messaging thread with Steve Jobs and Tim Cook. (This may cause a flutter in your heart and make you question your perception of reality, but it’s not really them.) This is the view you’ll inspect.

Even Steve Wozniak joins the fun!

Even Steve Wozniak joins the fun!

Go back to Xcode and click on the Debug View Hierarchy button in the Debug bar. Alternatively, go toDebug\View Debugging\Capture View Hierarchy.

Screenshot 1

Xcode is now interrupting your app and handing the reigns to the debugger, just as if you had paused your app with the pause button on the Debug bar.

In addition, Xcode replaces the code editor with a canvas in which it draws the entire view hierarchy of the key window of your app, including thin lines (called wireframes) that indicate the boundaries of every view.

Screenshot 2

You may know that when you add a subview to a view hierarchy, you’re adding a layer on top of the current stack of views. Because most views don’t overlap, it looks like all views are just part of one big layer when you run your app. The screen you’re currently looking at is pretty close to that, but with a bunch of extra lines.

So how is that useful? Well, right now you’re seeing a visual of the view stack from overhead, but what if you could visualize where the layers fall within the stack? Click and drag in the canvas, and you’ll see that instead of a flat 2D view, you’re actually interacting with a 3D model of your view hierarchy.

Screenshot 3

You can view the hierarchy from the side, top, bottom, a corner and even from the back!

views

Note: It’s possible your canvas isn’t showing the same views as this tutorial assumes. To make sure you’re on the same page, press cmd + 6 to get to the Debug navigator.

At the bottom of the pane, you’ll see two buttons on the left. Deselect both of these buttons, as seen in this image. If not, some views will be hidden on the canvas.

filter image

Exploring the View Hierarchy

The most natural and common perspective of this 3D model is to look at it from the left side — you’ll see why a little later in this tutorial — so manipulate it to get a point of view like the one below.

Screenshot 4

This gives your view hierarchy some perspective that’s very useful if you want to visualize how it’s built. However, there seems to be many empty views at the “bottom” (on the left) of the stack. What’s that all about?

Click on the left-most view (i.e. the one in the very back) and you’ll see that Xcode highlights it to indicate your selection. You’ll also see the Jump Bar (just above the Canvas) update to show aUIWindow as the last item — that last item will always reflect the currently selected view and its class type.

Screenshot 5

Since this app uses only one window, it’s safe to assume the UIWindow at the start of the Jump Bar is the app’s key window, or in other words, the window property on AppDelegate.

OK, it’s good to know how to find the window, but it’s unlikely that you’ll need to inspect that. What about the next view? In the canvas, click on the view to the right (i.e. on top) of the window and look at the Jump Bar again. UILayoutContainerView. That’s not even a public class!

iw21s

From there, the view hierarchy looks like this:

  1. UINavigationTransitionView: The container view in which navigation controller transitions happen
  2. UIViewControllerWrapperView: A wrapper view that contains the view controller’s view property
  3. UIView: The top-level view of a view controller (the same as a view controller’s view property)
  4. JSQMessagesCollectionView: The collection view used by this project to display all messages

Focusing on Views of Interest

In debugging this particular view hierarchy, the first four views (starting with the window) are really just visual noise. They don’t bring anything meaningful; they distract in trying to understand what else is going on in this view hierarchy. It would sure be nice if you could filter out that visual noise…

And you can. :] Look at the double-thumbed slider in the bottom right of the canvas. By default, the thumbs are all the way on the left and right side of the slider.

Screen Shot 2015-04-07 at 12.24.14 PM

Drag the left thumb slowly to the right a little bit; you’ll see the wireframe that represents the app’s window disappear from the canvas. If you drag a little further, the UINavigationTransitionView disappears as well.

Drag the left thumb as far as needed to hide all the parent views of JSQMessagesCollectionView. Your canvas should now look similar to this:

Screenshot 6

On the right side, you might notice the navigation bar is not just distracting, but it’s actually laid out on top of the collection view, making it hard to see what’s going on underneath. Fortunately, you can hide it.

Because you’re focusing on a smaller area of the screen with many smaller views that comprise the navigation bar, it’s a good idea to zoom in on the nav bar so you can see what exactly you’re doing.

Use the zoom control buttons, which are in a group of three buttons centered at the bottom of the canvas:

Screen Shot 2015-03-15 at 5.03.26 PM

As you would expect, the + button zooms in, the – zooms out, and the = button resets the zoom to the normal zoom level. Zoom in to get a good visual of the navigation bar.

Screenshot 7

Note: If you use a trackpad, pinch gestures will also zoom. A trackpad is also useful to move around in the canvas if all parts of the screen can’t be shown at once because you zoom in really far.

You can also zoom with option-mousewheel.

The extra detail you get by zooming in on the toolbar is nice, but the views still slightly overlap, so it’s not easy to tell which view is which.

To solve that problem, use the spacing slider in the bottom left corner of the canvas. The further you drag the thumb of the spacing slider to the right, the more spacing Xcode shows between different views.

Screen Shot 2015-04-07 at 12.29.20 PM

In this particular case, move the slider to the right as much as needed to avoid overlapping views in the toolbar. You might have to play around with the perspective by click-dragging on the canvas to get the desired result.

Screenshot 8

The 3D model is now perfectly manipulated so you can easily hide the navigation bar.

In the slider on the right (the one that hides views), drag the right thumb slowly to the left, up to theUINavigationBar. Remember that you can use the Jump Bar to identify each view’s class by selecting the topmost layer as you go. You’ll see the navigation items disappear first, then the buttons that contain them, followed by a few private views, and lastly, the navigation bar.

Look ma, no nav bar!

Look ma, no nav bar!

Note: If you rotate the canvas to look at the 3D view hierarchy model with the top layer on the left, the slider’s left thumb still removes views from the bottom of the stack, which is now on the right. Similarly, the right thumb removes views from the left.

Moving a slider from the left to the right and having views disappear from the right to the left (and vice versa) is counterintuitive, so that’s why looking at the model with the top layer on the right is the most natural perspective.

Unfortunately, hiding the nav bar (with the root view of _UIBackdropView) view also causes the toolbar items’ content at the bottom of the screen to disappear. To see this, you may need to adjust the zoom level or move down the canvas.

You want to see the toolbar items as they are an important part of the screen, so only hide the views up until (but not including) the _UIBackdropView. The navbar stack should look something like the following once you’re done.

Screenshot 9

More View Options

Now that irrelevant views are hidden, it’s time to take a look at the screen from the front again. You can try to drag the model back into place, but sometimes it’s difficult to get it just right. Fortunately, there is a simpler way.

Look at the group of four buttons to the left of the zoom buttons. The third button from the left is theResetViewing Area button. It undoes rotations and gives you the front perspective of the view hierarchy, just like in the simulator or on a device.

Screen Shot 2015-03-15 at 5.03.26 PM copy

Your canvas should look similar to the following at this point:
head on view with wireframes

You probably noticed that what you see in the debugger is still not exactly what you see when the app actually runs.

First of all, you still have wireframes around every single view; they allow you to see transparent views or views without any content, but if you don’t need the detail they can make things pretty noisy.

You can turn this off with the View Mode button — the one to the right of the Reset Viewing Areabutton. When you click the view mode button, you can specify if you want to see the views’ wireframes, their contents or both.

wireframes

A wireframes-only view is particularly useful if you’re mostly interested in positioning, and not so much about what a view looks like. Showing only the views’ contents is useful when you’re trying to debug a view’s appearance.

To reduce some of the clutter the wireframes cause in this case (especially near the navigation bar and toolbar), change the view mode to Contents to remove all the wireframes and leaves you with the core of the app.

no wireframes

Next, a couple of things are missing from the current view. When you run the app, you’ll see labels above the text bubbles that indicate the sender’s name or the message’s timestamp, as well as an image of the Golden Gate Bridge in the last bubble. But the debugger isn’t showing those labels and that image!

To solve this, look at the first button in the center row of buttons on the canvas. This is a toggle to show or hide clipped views. These are views that have their clipsToBounds property set to YES.

clip to bounds button

That is exactly what is happening with these labels, presumably because long names and dates should not extend beyond the bounds of their labels. The same applies to the image, which uses a corner radius and clipping to produce the rounded corners. Click on the toggle and you’ll notice these views now show up in Xcode.

Screenshot 11

Note: You may notice wireframes around the newly visible items. If that’s the case, toggle wireframes on and then off with the View Mode button you used previously. That should resolve the issue.

And there you have it: a near-perfect replica of your view hierarchy right within Xcode.

That was easy!

That was easy!

Inspecting Views

Now that the important parts are easily accessible, it’s time to look at layouts for these different views.

You already knew the collection view is what makes all these views come together, but wouldn’t it be great if you could just have an overview of the different elements that are at play here? Good news – you can!

Press cmd + 6 to go to the Debug navigator. Since this is a debugging session just like any other, the Debug navigator provides contextual information about the current session. For view debugging, this means Xcode provides a view tree of all the views for all windows. Expand the tree in the Debug navigator to look like this:

Screenshot 12

Note: At the bottom of the Debug navigator, you’ll see options that give a little bit of control over what kinds of items display in this tree. Apple’s documentation claims the button on the left filters private elements out of system view implementations, though this button does not seem to work as of Xcode 6.2.

The button to the right hides views that have their hidden properties set to YES, and the search bar only displays views and constraints that match the search criteria.

For the purposes of this tutorial, you should have both buttons deselected and no search filter applied.

This is a good point to start exploring a little bit. Expand the last JSQMessagesCollectionViewCellOutgoing. It only has one subview: a UIView.

If you’ve worked with collection views before, you know this makes sense because anyUICollectionViewCell has a contentView property that contains the cell’s contents.

Click — don’t expand — on that UIView in the Debug navigator and you’ll see that Xcode has now highlighted it in the canvas so you know exactly where it is on the screen.

Screenshot 13

To really understand how iOS positions that cell, open the Size Inspector with cmd + option + 4. The top part of the visualizes the view’s bounds, position and anchor point.

Screenshot 14

However, the really interesting part is the list of Auto Layout constraints that apply to this view. You can immediately tell that the cell’s content view is 312 points wide, 170 points tall and centered in its parent. The containing cell is also 312 by 170 points, so you know the content view takes up the entire cell.

Below are constraints colored in gray that indicate those are constraints that dictate relationships between this view and its subviews.

To get more details about a particular constraint, first expand that view in the view tree and then the Constraints item. You’ll also see the same constraints you saw in the Size navigator listed here.

Screenshot 15

Click on the first constraint (for me it’s a constraint on self.midX) and switch to the Object inspector by pressing cmd + option + 3. You’ll see a constraint overview with the items, multiplier, constant and priority. This is much like the summary you see in Interface Builder when you edit a constraint.

Screenshot 16

In addition to sizing and constraint information, you can see other details related to the display of a particular view in the object inspector. Back in the Debug navigator, expand the UIView in the tree and you’ll see there are three JSQMessageLabels and two UIViews under it. Select the first JSQMessageLabel(the one with the timestamp), and open the Object inspector.

Screenshot 17

The first section indicates the object’s class name and memory address, and the second shows values for various public properties of the object that pertain to its appearance.

Here you can see the label’s text color is 0.67 gray with no alpha, and its font size is 12pt.
Other classes have useful information specific to how they’re visualized as well. Back in the Debug navigator, expand the second UIView under the cell’s root UIView and you’ll see a UIImageView.

Screenshot 18A

Select the image view from the tree and check out the Object inspector.

Screenshot 18B

Here you’re looking at the view that shows the user’s avatar — in this case, the author’s initials, JSQ. You can see the normal image, conveniently labeled Image, as well as the darker image, labeled Highlighted, that shows when the user taps on the cell.

The other two instances of JSQMessageLabel in this cell’s root view don’t currently have text, but they are used for the sender’s name in incoming messages and an error message when sending an outgoing message fails.

And that’s how easy it is to do view debugging in Xcode! To continue running the app, just click the Continue button on the Debug bar, or go to Debug\Continue, just like you would if you were in a normal debug session.

Live Modifications

Now that you know the basics of view debugging in Xcode 6, it’s time to put that knowledge to work with a small exercise: For the collection view you’ve been exploring throughout the tutorial, make its vertical scroll indicator red by only using the debugger.

Here are two tips to get you started:

  1. Since view debugging is just like any other debugging session, you can use expr and other commands in the console, but keep in mind changes won’t appear until you resume execution. For more information on these commands, take a look at this debugging apps in Xcode tutorial.
  2. Because pointers in Objective-C are really just memory addresses, you’re simply messaging a memory address when you message an object. This works in the debugger as well, so a command like po 0x123456abcdef would print the description of the object at that memory address.

Give it some solid attempts before you break down and look at the solution below!

Solution Inside: Solution Show
 
   
 


Congratulations, you now know the basics of view debugging with Xcode 6!

Old School Debugging

Live view debugging made debugging views in Xcode 6 a lot easier, but that doesn’t mean that your favorite old school tricks are now useless. In fact, iOS 8 introduces a very welcome addition to the family of view debugging tricks: _printHierarchy.

Note: You’ve already learned the basics of view debugging with Xcode 6, so feel free to skip this optional section if you’d like. However if you’re curious about some handy old-school techniques, read on!

Printing the View Controller Hierarchy

_printHierarchy is a private method on UIViewController that you can use to print the view controllerhierarchy to the console. Build and Run, select Push via storyboard and then hit the pause button in the Debug bar.

Now type this in the console and press return:

po [[[[UIApplication sharedApplication] keyWindow] rootViewController] _printHierarchy]

You’ll get something very similar to this:

<UINavigationController 0x7fdf539216c0>, state: appeared, view: <UILayoutContainerView 0x7fdf51e33bc0>
   | <TableViewController 0x7fdf53921f10>, state: disappeared, view: <UITableView 0x7fdf5283fc00> not in the window
   | <DemoMessagesViewController 0x7fdf51d7d520>, state: appeared, view: <UIView 0x7fdf53c0b990>

This tells you that there is a UINavigationController whose first view controller is a TableViewController — the one where you chose how to push the controller. The second view controller is theDemoMessagesViewController, or the view controller you have been debugging.

It doesn’t seem too exciting in this particular example, but if you have several child view controllers within a navigation controller, and a tab bar controller in a popover in a modal view controller, (I’m not proud of some of my apps’ UI…) it can be immensely useful for figuring out exactly how the view controller hierarchy works.

Printing the View Hierarchy

If you’re not a very visual person and prefer a textual overview of a view hierarchy, you can always use the age-old, and also private, recursiveDescription on UIView. This prints a view hierarchy very similar to the view controller hierarchy as demonstrated above.

Open Views\JSQMessagesCollectionViewCellOutgoing.m and add a breakpoint in awakeFromNib.
breakpoint

Build and Run, then select Push via Storyboard. The debugger should break as aJSQMessagesCollectionViewCellOutgoing is loaded. Now type the following into the console:

po [self.contentView recursiveDescription]

This will print the hierarchy of the JSQMessagesCollectionViewCellOutgoing’s contentView, which will look something like this:

<UIView: 0x7fde6c475de0; frame = (0 0; 312 170); gestureRecognizers = <NSArray: 0x7fde6c484fe0>; layer = <CALayer: 0x7fde6c474750>>
   | <JSQMessagesLabel: 0x7fde6c475eb0; baseClass = UILabel; frame = (0 0; 312 20); text = 'Today 10:58 PM'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fde6c476030>>
   | <JSQMessagesLabel: 0x7fde6c476400; baseClass = UILabel; frame = (0 20; 312 0); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fde6c476580>>
   | <UIView: 0x7fde6c476b50; frame = (70 20; 210 150); autoresize = RM+BM; layer = <CALayer: 0x7fde6c474dd0>>
   |    | <UIImageView: 0x7fde6c482880; frame = (0 0; 210 150); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fde6c476ae0>> - (null)
   | <UIView: 0x7fde6c482da0; frame = (282 140; 30 30); autoresize = RM+BM; layer = <CALayer: 0x7fde6c482d00>>
   |    | <UIImageView: 0x7fde6c482e70; frame = (0 0; 30 30); opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7fde6c482f70>> - (null)
   | <JSQMessagesLabel: 0x7fde6c483390; baseClass = UILabel; frame = (0 170; 312 0); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fde6c483510>>

It’s rudimentary, but can be helpful when you want to debug a view hierarchy pre iOS 8.

Using debugQuickLookObject

Lastly, Xcode 5.1 introduced a feature called Debug Quick Look. This feature is most useful when you’re already debugging and aren’t wondering what an object looks like at a certain point in your code.

Your custom class can implement the method debugQuickLookObject and return anything that is visually presentable by Xcode. Then, when you’re debugging and you have an object you want to inspect, you can use quick look and Xcode will show you a visual representation of that object.

For example, NSURL’s implementation of debugQuickLookObject returns a UIWebView with that URL so you can actually see what’s behind the URL.

For more information on debugging using Quick Look, take a look at the documentation.

Where To Go From Here?

And that’s it for Live View Debugging. It’s an easy tool and can save hours of manually sifting through a view hierarchy trying to understand how and where it’s drawing views.

If you’re looking for a more advanced and comprehensive tool than just Xcode, then take a look at Reveal. While it’s a paid app, it’s also more powerful than Xcode’s view debugging. You can view our Tech Talk on the subject over here.

We hope you enjoyed this tutorial and feel a little more comfortable debugging your UI. If you have comments or questions, please use the forum discussion below!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值