iOS DeveloperiOS 开发

自动布局指南-Part 3:调试自动布局

2016-09-08  本文已影响246人  lakerszhy

翻译自“Auto Layout Guide”。

3 调试自动布局

3.1 错误类型

自动布局中的错误主要分为三个类型:

大多数时候,实际问题仅仅是决定什么出错了。你添加了你任务需要的约束,但是运行程序时,并不是你期望的结果。

通常,只要你理解了问题,解决方案就很明显了。移除冲突的约束,添加缺少的约束,以及微调优先级就会解决问题。当然,要容易的理解问题出在哪里,可能需要经理一些试验和错误。更其它技巧一样,孰能生巧。

但有时事情会变得更复杂。此时需要“调试的技巧和窍门”。

3.2 不可满足的布局

当系统不能为当前约束集找到一个有效解时,会产生不可满足的约束。两个或多个必需约束的冲突,因为它们不能同时为真。

3.2.1 识别不可满足的约束

通常,界面生成器可以在设计时检测到冲突。此时,界面生成器有几种方式显示错误:

提示
尽管界面生成器提供的实时反馈可以更容易创建有效的布局,但它不能找出所有可能的布局错误。
例如,界面生成器只能在画布的当前尺寸检测冲突;然而有些冲突只发生在根视图被拉伸或压缩到某个点(或者当内容扩展或压缩到某个点)。界面生成器不能检测到这些错误。
所以,尽管你总是修复界面生成器识别的所有问题,但修复明显的错误还是不够的。你还需要执行所有屏幕尺寸,方向,动态类型尺寸(dynamic type sizes)和支持语言的运行时测试(runtime testing)。

当系统在运行时检测到不可满足的布局时,执行以下步骤:

  1. 自动布局识别冲突约束集。
  2. 它打破其中一个冲突的约束,并检查布局。系统继续打破约束,直到找到一个有效的布局。
  3. 自动布局在控制台记录冲突和打破的约束的信息。

这个后备系统让应用程序继续运行,同时尝试显示有意义的东西给用户。但是不同布局,甚至构建,打破约束的效果多种多样。

在很多情况下,缺少约束可能不会有任何可视化效果。视图层次结构按你期望的那样显示。其它情况下,缺少约束可能导致视图层级结构整体错位,尺寸不对,或者完全不显示。

当它们没有明显效果时,通常会暂时忽略错误——毕竟,它们没有改变应用程序的行为。然而,对视图层级结构或者SDK的任何改变也会改变打破的约束集,突然产生一个明显的打破布局。

因此,检测到不可满足的约束时,总是修复它们。为UIViewAlertForUnsatisfiableConstraints设置一个象征断点,确保测试时捕获不明显的错误。

3.2.2 阻止不可满足的约束

不可满足约束相对容易修复。当不可满足约束产生时,系统会通知你,并提供冲突约束的列表。

当你知道错误后,解决方案通常会很简单。移除其中一个约束,或者修改为可选约束。

但是,有一些常见问题值得进一步检查:

3.3 有歧义的布局

有歧义的布局发生在系统的约束有两个或多个解时。有两个主要原因:

3.3.1 检测有歧义的布局

与不可满足的布局一样,界面生成器通常可以在设计时检测和提供建议,来修复有歧义的布局。这些歧义显示在问题导航器的警告,文档大纲的错误和画布中的红色线条。更多信息请参考“识别不可满足的约束”。

与不可满足的布局一样,界面生成器不能检测到所有可能的歧义。许多错误只能通过测试发现。

当运行时发生有歧义的布局时,自动布局选择使用一个可能的解。这意味着布局可能或者不可能如你期望的那样显示。此外,控制台没有警告,不能为有歧义的布局设置断点。

因此,有歧义的布局比不可满足的布局更难检测和识别。即使歧义有很明显的可视化影响,也很难检测错误是因为歧义还是布局的逻辑错误。

幸运的是,你可以调用一些方法来帮助识别有歧义的布局。所有这些方法只在调试中使用。在能访问视图层级结构的地方设置断点,然后在控制到调用以下其中一个方法:

在控制台运行这些命令时,可能需要使用Objective-C语法。例如,断点暂停执行后,在控制台窗口输入call [self.myView exerciseAmbiguityInLayout],调用myView对象的exerciseAmbiguityInLayout方法。类似的,输入po [self.myView autolayoutTrace],打印包括myView的视图层级结构的诊断信息。

提示
运行以上诊断方法之前,确保修复了界面生成器找到的所有问题。界面生成器试图修复它找到的任何错误。这意味着,如果它找到一个有歧义的布局,它会添加约束,让布局不再有歧义。
因此hasAmbiguousLayout会返回NO。exerciseAmbiguityInLayout没有任何效果,constraintsAffectingLayoutForAxis:可能返回额外的,出乎意料的约束。

3.4 逻辑错误

逻辑错误是简单的bug。某些地方,可能与错误有关。它可能与自动布局如何计算视图的frame有关。它可能与你创建的约束集,或者与你设置的视图属性有关。它可能与约束如何与创建复杂行为的交互有关。不管怎么样,有些事情有些地方不匹配你的心理模型。

逻辑错误是最难查找的。排除所有其它可能性后,剩下的即使再不可能,也肯定是逻辑错误。但是,即使已经检测到有一个bug,你仍然需要找到错误发生在哪里。

这里没有工具或者步骤介绍。修复逻辑错误通常涉及实验和迭代测试,用来识别问题和指出如何修复。但是,这里有一些建议可能会帮到你:

3.5 调试技巧和窍门

以下主题描述收集和组织布局信息的技术,也描述你可能遇到的意外行为。你可能不需要再每一个布局上使用这些技术,但它们可以帮助你解决最难的问题。

3.5.1 理解日志

视图的信息被打印在控制台,不管因为有一个不可满足的布局,或者因为你使用constraintsAffectingLayoutForAxis:或constraintsAffectingLayoutForOrientation:调试方法显式打印约束。

不管哪里方法,你都能在这些日志中找到很多有用的信息。这是一个不可满足布局的错误输出:

2015-08-26 14:27:54.790 Auto Layout Cookbook[10040:1906606] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7a87b000 H:[UILabel:0x7a8724b0'Name'(>=400)]>",
    "<NSLayoutConstraint:0x7a895e30 UILabel:0x7a8724b0'Name'.leading == UIView:0x7a887ee0.leadingMargin>",
    "<NSLayoutConstraint:0x7a886d20 H:[UILabel:0x7a8724b0'Name']-(NSSpace(8))-[UITextField:0x7a88cff0]>",
    "<NSLayoutConstraint:0x7a87b2e0 UITextField:0x7a88cff0.trailing == UIView:0x7a887ee0.trailingMargin>",
    "<NSLayoutConstraint:0x7ac7c430 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7a887ee0(320)]>"
)
 
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7a87b000 H:[UILabel:0x7a8724b0'Name'(>=400)]>
 
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

这些错误信息显示了5个冲突约束。同一时间,这些约束不全为真。你需要移除一个,或者转为可选的约束。

幸运的是,视图层级结构相对简单。有一个父视图包括一个标签和一个文本框。冲突约束设置了以下关系:

  1. The label’s width is greater than or equal to 400 points.
  2. The label’s leading edge is equal to the superview’s leading margin.
  3. There is an 8-point space between the label and the text field.
  4. The text field’s trailing edge is equal to the superview’s trailing margin.
  5. The superview’s width is set to 320 points.

系统尝试通过打破标签的宽度来恢复。

提示
约束使用Visual Format Language写入控制台。即使你从来没有使用过Visual Format Language创建自己的约束,也必须可以阅读和理解它,来有效的调试自动布局问题。更多信息请参考“Visual Format Language”。

这些约束中的最后一个由系统创建,你不能修改它。此外,它创建了一个与第一个约束明显的冲突。如果父视图只有320个点宽,你就不能使用400个点宽的标签。幸运的是,你不需要去掉第一个约束。如果把它的优先级降为999,系统仍然尝试提供选择的宽度——尽可能接近,同时仍然满足其它约束。

基于视图的autoresizing mask(例如,当translatesAutoresizingMaskIntoConstraints为YES时创建的约束)的约束有额外的mask信息。放置约束后,日志字符串显示h=,后面跟着三个字符,和v=,后面跟着三个字符。-(连字符)表示一个固定值,&表示可变值。对于水平mask(h=),三个字符表示左边页边留白,宽度和右边页边留白。对于垂直mask(v=),它们表示顶部页边留白,高度和底部页边留白。

例如,考虑以下日志信息:

<NSAutoresizingMaskLayoutConstraint:0x7ff28252e480 h=--& v=--& H:[UIView:0x7ff282617cc0(50)]>"

该信息由以下几部分组成:

3.5.2 为日志添加标识符

尽管上一个例子理解起来相对简单,但更长的约束列表变得很难理解。你可以为每个视图和约束提供一个有意义的标识符,让日志更容易阅读。

如果视图有明显的文本组件,Xcode用它作为标识符。例如,Xcode使用标签的文本,按钮的标题,或者文本框的占位符来标记这些视图。否则的话,在标记检查器(Identity inspector)中设置视图的Xcode具体标签。界面生成器在它的界面中使用这些标识符。许多标识符也在控制台日志中显示。

对于约束,通过代码或者属性检查器设置它们的identifier属性。当在控制台打印信息时,自动布局使用这些标识符。

例如,这里是同一个,设置了标识符的不可满足的约束错误:

2015-08-26 14:29:32.870 Auto Layout Cookbook[10208:1918826] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7b58bac0 'Label Leading' UILabel:0x7b58b040'Name'.leading == UIView:0x7b590790.leadingMargin>",
    "<NSLayoutConstraint:0x7b56d020 'Label Width' H:[UILabel:0x7b58b040'Name'(>=400)]>",
    "<NSLayoutConstraint:0x7b58baf0 'Space Between Controls' H:[UILabel:0x7b58b040'Name']-(NSSpace(8))-[UITextField:0x7b589490]>",
    "<NSLayoutConstraint:0x7b51cb10 'Text Field Trailing' UITextField:0x7b589490.trailing == UIView:0x7b590790.trailingMargin>",
    "<NSLayoutConstraint:0x7b0758c0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7b590790(320)]>"
)
 
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7b56d020 'Label Width' H:[UILabel:0x7b58b040'Name'(>=400)]>
 
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

正如你所看到的,这些标识符让你可以快速的,容易的在日志中识别出约束。

3.5.3 可视化视图和约束

Xcode提供了工具,帮助你在视图层级结构中可视化视图和约束。

要在模拟器中查看这些视图:

  1. 在模拟器中运行应用程序。
  2. 切换回Xcode。
  3. 选择Debug > View Debugging > Show Alignment Rectangles。该设置画出视图边缘(edges)的轮廓。

对齐的矩形框是自动布局使用的边缘。打开这个选项,可以快速认出所有意外尺寸的对齐矩形。

如果你需要更多信息,在Xcode调试栏中点击Debug View Hierarchy(

)。然后Xcode显示一个可交互的视图调试器,提供一些工具用于探索和与视图层级结构交互。调试自动布局问题时,“Show clipped content”和“Show constraints”选项尤其有用。

启用”Show clipped content“选项,会显示不小心放到屏幕外的视图的位置。启用”Show constraint“选项会显示影响当前选中视图的所有约束。当事情开始变得古怪时,两个选项提供了快速明智的检查。

更多信息请参考”Debug Area Help“中的”Debugging Views“。

3.5.4 理解边缘情况

这里是一些边缘情况,导致自动布局以意外的方式表现:

上一篇 下一篇

猜你喜欢

热点阅读