开发者会常常遇到视图或者Auto Layout约束中存在bug的状况,而且这种bug很难经过代码发现,因此开发者颇有必要熟知如何进行简单高效的视图调试,而Xcode 6的发布使得视图调试变得史无前例的简单。html
开发者不用将frames打印到控制台,而后在脑海中可视化视图的布局,如今你能够在Xcode中查看整个视图层次。ios
本教程会带你熟悉全部能够操做的不一样选项。你作好写代码的准备了吗?这个问题有点使人烦心,由于你根本就不想写代码。你能够在Xcode 6中检查开源库的视图层次,了解它的编写方法---最重要的是不用看任何代码。git
开始github
本教程使用Jesse Squires 编写的JSQMessagesViewController库,库的UI看起来很是熟悉,相似Messages app。canvas
先在 GitHub project page下载源码并解压。xcode
注意: 该库使用了CocoaPods来管理和其余库之间的依赖关系,不熟悉的话能够先看看CocoaPods tutorial。app
接着在终端找到解压项目,运行 pod install 安装所需依赖关系。打开JSQMessages.xcworkspace并编译,而后在iPhone5s模拟器中运行应用程序。 (可以使用任意尺寸的模拟器,本教程中使用的是4英寸的模拟器,你能够选择4英寸模拟器以便于快速了解教程内容)编辑器
注意: Live View Debugging仅支持在iOS 8上运行应用程序,不支持iOS 7,即使你用的是Xcode 6。ide
点击项目中的 Push via storyboard选项 进入与Steve Jobs 和Tim Cook消息发送界面线程,这就是你将要查看的视图。工具
Even Steve Wozniak joins the fun!
回到Xcode并点击调试栏中的Debug View Hierarchy按钮 ,或者经过Debug\View Debugging\Capture View Hierarchy操做,效果是同样的。
Xcode会打断app的运行并进行调试,该操做和你使用调试栏上的的"pause"按钮暂停app运行同样。此外,Xcode会展现canvas(译者注:如下简称"画布")而不是代码编辑器。Xcode在canvas上绘制了app主窗口的整个视图层次,包括指示每一个视图边界的细线(称之为线框图)。
若是往视图层次上添加一个子视图,也就是在当前的视图堆栈上添加layer。因为大部分views不会叠加,因此运行app时,全部的views看起来像是大layer的一部分,下图很是接近这种描述,不过带着一些额外的线。
如今你所看到的是一个可视化的视图堆栈。在canvas中点击并拖动,会看到视图层次的3D模型。
你能够从上、下、左、右多个角度查看视图层次。
注意:在尝试的过程当中,canvas可能不会像教程中展现的这样。为确保你是在同一个页面,请按下cmd + 6 调出Debug navigator。
在面板底部左侧有两个按钮。以下图所示,取消对这两个按钮的选定,不然会隐藏一些视图。
探索视图层次(Exploring the View Hierarchy)
最天然最经常使用的方式是从左边开始探究3D模型,稍后教程为解释为何要这么作。向左拉动视图层次,以下图所示:
若是你想可视化app的构建,那么从多个角度查看视图层次就很是有用了。不过,在堆栈的底部(左边)有不少空视图,它们是什么呢?
点击最左边的视图(也就是最后边的视图),Xcode会对其进行高亮。画布上方的Jump Bar(跳转栏)更新展现一个UIWindow做为最新项目--最新项目一般指出了当前选中的项目以及其class类型。
因为该app只使用一个窗口,因此可假定位于跳转栏前边的UIWindow 是app的主要窗口,也就是AppDelegate的 window 属性。
不过,彷佛检查这个图层并无多大意义。那下一个视图呢?在画布中,点击窗口最右边的视图(也就是最上边的视图),再看看跳转栏(Jump Bar)看看有什么不同的。UILayoutContainerView,甚至不是一个公开类。
这时候,视图层次看起来是这样的:
1.UINavigationTransitionView:导航控制器在这里发生转场行为的容器视图。
2.UIViewControllerWrapperView: 包含view controller 的view属性的封装视图。
3.UIView: view controller的最上层视图 (与view controller的view 属性一致)
4.JSQMessagesCollectionView: 工程使用collection view来展现全部消息。
Focusing on Views of Interest(关注与调试相关的视图)
在调试该特定视图层次时,头四个视图(从窗口开始)其实是视觉"噪音",它们并没有多大意义,会让你分心,若是能过滤掉这些"噪音"视图就最好不过了。
你固然能够这么作!在画布的右下角有一个双滑块儿滑杆,左右滑动滑块可帮你暂时隐藏一些视图,默认状况下,滑块在滑杆的左右两端。
将滑块左端滑块向右滑动一点,画布中app的线框图会一层层消失。将滑块儿拖得更远一点,UINavigationTransitionView也消失不见了。
根据须要将左端滑块儿尽量拖得远一些,从而隐藏JSQMessagesCollectionView的父视图。你的画布看起来应该和下边相似:
在右侧,你会发现导航栏彷佛有点让人分心,可是它的的确确位于collection view的最上层,不方便查看下层布局。幸运的是你能够隐藏它。
因为你主要关于屏幕上较小的一个区域,而且导航栏上还有不少比导航栏更小的元素。这时候放大导航栏可让你更清楚地看到界面是如何布局的。
使用缩放控件按钮,它是一组三个的按钮,居中展现在画布中。
这一组按钮有放大"+"、缩小"-"以及将视图重置到正常的水平的"="三种选择,
注意:若是你使用的是触控板,两指捏合和缩放也能缩小和放大视图。若是你一次性缩放过多,那么屏幕上的内容就不能彻底展现,这时候使用触控板仍是比较有用的。固然你也可使用鼠标滑轮进行缩放。
虽然经过缩放toolbar得到额外的细节很是不错,但这些视图仍牢牢地叠加在一块儿,要分清谁是谁并不容易。
想要解决这个问题,可使用画布左下角的spacing slider,向右拖动圆形滑块儿越远,不一样同视图之间的间距。视图间的间距会随着滑块儿拖动的距离增长而变大。
在该案例中,尽量地向右移动滑杆儿,以免工具栏中视图叠加。你能够试试在画布上拖放视图以达到理想的效果。
画布右下角隐藏视图的滑杆,将右端滑块儿移至左端,直到剩下UINavigationBar。选择最上边的图层,你可使用Jump Bar来辨认每一个视图的类。首先你会看到导航项目消失了,接着是包含它们的按钮,而后是一些私有视图,最后是导航栏。
啊?没有导航栏了!
注意:旋转画布查看3D视图层次,若是最顶层视图位于左侧,那么滑杆儿的左侧滑块儿依旧从堆栈底部移除视图,如今是在右侧。一样,无论顶层视图在左侧仍是右侧,右侧滑块儿都从堆栈的顶部开始移除。
将滑块儿从左侧移至右侧,视图从右向左逐渐消失(反之亦然),这是有违直觉的,这也是让顶层视图位居右侧,这种查看3D模型的方式才是最天然的方式,就是咱们在教程最初提到的那一点。
不幸的是,隐藏导航栏(包含_UIBackdropView根视图)视图也会让屏幕底部toolbar里的内容消失。调整缩放度或向下移动画布才能看到发生的变化。
因为toolbar项目是屏幕上重要的一部分,你须要查看这些内容,因此仅仅隐藏视图直到(但不包括)剩下_UIBackdropView,导航栏堆栈看起来像是下边这种:
More View Options(更多视图选项)
如今已经隐藏了不相关的视图,咱们接着要从正面再看这个屏幕。你能够将3D模型拖回原位,不过有时候很难刚恰好,还好咱们有其余办法。
在3D模型的下方---缩放按钮的左侧有一组四个按钮,从左向右数第三个按钮是ResetViewing Area按钮,它能够取消旋转并从正面展现视图层次,像在模拟器或者设备上同样。
画布看起来应该和下边的同样:
你可能注意到你在调试器中所见到的,并不全是app实际运行时的样子。
首先,单个视图周围易燃有线框图包围,它们可让你查看透视图或者没有任何内容的视图,不过若是你不须要细节信息,线框图就会让事情变得杂乱。
你可使用View Mode按钮进行选择-在Reset Viewing Area按钮的右侧。点击视图模式按钮,你能够选择只查看线框图、只查看内容或者同时查看二者。
若是你主要是对位置感兴趣,而且不大关心视图看起来什么样子,那么线框图是很是有用的。当你想要调试视图的外观时,仅展现视图内容就很是有用了。
想要减小线框图引发的杂乱(尤为是靠近导航栏和toolbar的地方),可将查看模式更改成Contents ,以移除全部线框图,仅留下app的核心部分。
接下来,是从当前视图中忽略的几点。
当你运行app时,你将会看到文本气泡上的标签,指示信息发送者名称或者信息的时间戳,以及最后一个气泡中Golden Gate Bridge的图片。可是调试器并不会展现这些标签和图片。想要解决这个问题,可查看画布上中间一组按钮的第一个,可展现或者隐藏省略掉的视图。这些视图的clipsToBounds属性设置为了YES。
这是标签相关的,大概因为较长的名字和日期不该当延伸到标签的界限以外。这一点一样应用于图片,图片使用圆角半径并剪切以生成圆角图片。点击该按钮,你会注意到这些视图将再也不出如今Xcode中。
注意:你可能注意到可视项目周围仍有线框图,若是是这种状况,使用你先前使用的View Mode按钮
打开和关闭线框图,问题便可解决。
你已经都了解了:在Xcode中近乎完美地复制了视图层次。
So easy!
Inspecting Views(查看视图)
已经了解了最重要的部分,如今看看这些不一样视图的布局。
你已经知道了collection view如何让这些视图汇集在一块儿,可是若是能看到这些不一样元素的总体结构就更好了。固然能够啦!
按下cmd + 6 调出Debug navigator,和其余调试会话同样,Debug navigator提供了当前会话的文本信息。对于视图调试来讲,这意味着Xcode提供了全部窗口中视图的视图树。展开Debug navigator的视图树后是这个样子的:
注意:在Debug navigator的底部,你会看到一个选项能够控制在视图树中展现哪一种类型的项目。苹果的文档表示左边的按钮将系统视图实现的私有元素过滤出来,不过这个按钮在Xcode 6.2中彷佛不起做用。
右边按钮可隐藏那些将其 hidden 属性设置为YES的视图,而且搜索栏仅展现匹配搜索条件的视图和约束。
出于本教程的目的,取消对这两个按钮的选择,而且不使用任何搜索过滤。
这是个不错的而开始。展开最后一个JSQMessagesCollectionViewCellOutgoing,它只有一个子视图UIView。若是你之前使用过collection view,那你应该知道这个是讲得通的,由于任何UICollectionViewCell都有一个包含cell内容的contentView 属性。
点击但不要展开-将鼠标放在Debug navigator的UIView 上,你会看到Xcode已经在画布上对其高亮,这样你就准确知道它在屏幕的什么地方。
想要真正了解iOS如何放置该cell,可以使用cmd + option + 4打开Size Inspector,该导航器的顶部形象化了视图的边界、位置以及锚点。
不过,真正有趣的部分是应用于该视图的Auto Layout约束列表。你能够马上将cell的内容视图的宽和高分别设定为312 point和170 point,并将其居中。封闭的cell一样是312*170 point,因此内容视图占据了整个cell。下边用灰色显示的约束表示它们是指出视图和其子视图之间关系的约束。
想了解一个特定约束的更多细节,首先要展开视图树中的视图,而后展开Constraints项目。你将会看到和Size navigator中同样的约束列表。
点击第一个约束(self.midX的约束),并经过cmd + option + 3切换至Object inspector。你会看到一个约束概览。编辑约束时,这一点很是相似于Interface Builder。
除了尺寸和约束信息,你还会看到Object Inspector中特定视图的其余信息。回到Debug navigator,展开视图树中的UIView ,你会到它有3个JSQMessageLabel和两个UIView。选中第一个JSQMessageLabel(带有时间戳的那个),并打开Object Inspector。
第一个部分展现了对象的类名称和内存地址,第二部分展现了对象的多个公有属性的值。
从图中看出,标签文本的颜色是无alpha 的0.67灰,字体大小是12pt。
针对它们如何被可视化,其余类也有一些有用的信息。回到Debug navigator,展开cell的根UIView 中的第二个UIView,你会看到一个UIImageView。
从视图树中选择image view,并查看Object inspector。
你正查看的是展现用户头像的视图-在该例子中是做者的首字母JSQ,你会看到常规图片、便利的贴有标签的图片、较暗的图片以及被标记的高亮,这些将在用户点击cell时展现。
在cell的根视图中,JSQMessageLabel的其余两个实例当前尚未文本,但它们被用于即将进来的消息发送者的名字和消息发送失败时的错误信息。
怎么样,在Xcode中调试视图很是简单吧,继续运行该app,点击Debug bar上的"Continue"按钮,或者执行Debug\Continue,就像你在常规调试中那样。
Live Modifications(实时调整)
如今你已经了解了使用Xcode 6进行视图调试的基础支持,接下来将你所学应用到一个小小的实验中:只使用调试器,确保你在本教程中使用的collection view的垂直滚动指示器为red。
你能够从如下两点开始:
1.因为视图调试和其余调试部分很是像,你能够在终端使用expr 和其余命令,可是须要从新运行项目才能看出所作的变化。更多关于这些命令的信息,请查看debugging apps in Xcode教程。
2.因为Objective-C中的指针仅仅是内存地址,因此当你发送对象时,你仅仅发送了一个内存地址。这一样适合于调试器,因此相似po 0x123456abcdef这样的命令会打印出内存地址中的对象描述。
多作几回尝试,若是出现问题,可尝试如下解决方案:
首先要确保将视图模式设置为"Contents"
在Debug navigator中,展开collection view的视图树,以便清楚知道视图的子视图。
collection view的最后视图是两个UIImageView实例,是水平方向和垂直方向上的滚动指示器。点击第二个,你会看到水平方向上的指示器在画布中被高亮。
从Object inspector中复制图片视图的内存地址。
在这个例子中,滚动指示器的内存地址是0x7fde6c484640。须要作的是给该地址中的对象发送一个setBackgroundColor:信息可将滚动指示器着色。可以使用如下代码:
1
|
expr
(
void
)
[
0x7fde6c484640
setBackgroundColor
:
[
UIColor
redColor
]
]
|
继续运行该应用程序,在滚动collection view时,滚动指示器变成了红色。
祝贺你,你已经了解了使用Xcode 6进行视图调试的基本内容。
Old School Debugging(保守调试)
实时调试让使用Xcode 6进行视图调试变得很是简单,但并不意味你以前经常使用的调试方法已经没有用武之地了。事实上,除了视图调试外,iOS 8引入了一个很是受欢迎的技巧: _printHierarchy.
注意:你已经了解了Xcode 6视图调试的基本内容,因此若是你不喜欢能够直接跳过这个章节。不过若是你很是痴迷一些便捷的旧的技术,那不要错过这个。
打印View Controller Hierarchy
_printHierarchy是 UIViewController 的一个私有方法,你能够用它将view controller 层次打印到控制台。编译并运行,选中Push via storyboard,而后点击Debug bar上的"pause"按钮。
在终端打出如下内容并从新运行:
1
|
po
[
[
[
[
UIApplication
sharedApplication
]
keyWindow
]
rootViewController
]
_printHierarchy
]
|
这告诉你UINavigationController的第一个视图控制器是一个TableViewController,你能够选择如何推出控制器。第二个view controller是DemoMessagesViewController,或者你已经调试过的view controller。
这个例子彷佛不怎么使人兴奋,但若是你的导航控制器中有几个view controller,或者弹出视图中有tab bar控制器,那么想要弄清楚这些view controller如何工做, 这个功能就很是有用了。
Printing the View Hierarchy(打印视图层次)
若是你更喜欢文本化的视图层次,那你可使用UIView旧的私有recursiveDescription。这个方法打印出来的视图层次很是相似于上边描述的view controller层次。
打开Views\JSQMessagesCollectionViewCellOutgoing.m 并在awakeFromNib中添加一个断点。
编译并运行,而后选择Push via Storyboard。调试器有问题了,由于加载了JSQMessagesCollectionViewCellOutgoing。如今在控制台输入如下代码:
1
|
po
[
self
.
contentView
recursiveDescription
]
|
这将打印出 JSQMessagesCollectionViewCellOutgoing的contentView层次, 看起来像这样:
这是基本的,但能够帮你调试iOS 8以前的视图层次。
Using debugQuickLookObject(使用debugQuickLookObject)
最后,Xcode5.1引入了Debug Quick Look功能。若是你已经作好了调试的准备,但不大想知道某段代码如何实现对象的特定外观,那么这时候这个功能就很是有用了。
你的自定义类能够实现debugQuickLookObject方法,并返回任何由Xcode展现的内容。此外,若是你已经正进行调试,而且已经有了想要查看的对象,你可使用快速查看功能,而且Xcode会以可视化形式表示对象。
好比,NSURL对 debugQuickLookObject的实现返回了一个带有URL 的UIWebView,你能够真实看到URL背后的东西。
关于使用Quick Look调试的更多信息,请查看相关文档。
下一步
以上是关于实时调试的内容,它是一个能够帮助节省大量时间的工具,很是好用。