一、背景
当我们使用autolayout进行约束时,经常会遇到约束冲突或缺失问题,尤其是用到stackview进行布局,嵌套较深时很容易出现问题,排查起来也比较头疼,这里总结一下自己的排查经验,旨在提高排查效率,快速定位异常原因和控件。
二、排查方式
- 一般来说,约束异常在控制台会有警告信息,如:Unable to simultaneously satisfy constraints 开头的警告。以下面这个警告为例:
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. [ <NSLayoutConstraint:0x14c689450 YLBtnView:0x147a5bc00.width == YLBtnView:0x147a5bc00.height * 8.33333>, <NSLayoutConstraint:0x14c689ef0 YLBtnView:0x147a5bc00.height == 45>, <NSLayoutConstraint:0x14c68a210 UILayoutGuide:0x14beb1ea0.trailing == UIStackView:0x14c523100.trailing>, <NSLayoutConstraint:0x14c6891d0 UIStackView:0x14c523100.leading == UILayoutGuide:0x14beb1ea0.leading>, <NSLayoutConstraint:0x147a15cc0 UIStackView:0x14c523100.leading == YLBtnView:0x147a5bc00.leading>, <NSLayoutConstraint:0x147b04410 UIStackView:0x14c523100.trailing == YLBtnView:0x147a5bc00.trailing>, <NSLayoutConstraint:0x147b07bb0 UIView:0x14c621e00.width == 414>, <NSLayoutConstraint:0x14c689f90 UILayoutGuide:0x14beb1ea0.left == UIView:0x14c621e00.left>, <NSLayoutConstraint:0x14c688730 UIView:0x14c621e00.right == UILayoutGuide:0x14beb1ea0.right> ] Will attempt to recover by breaking constraint <NSLayoutConstraint:0x14c689450 YLBtnView:0x147a5bc00.width == YLBtnView:0x147a5bc00.height * 8.33333> - 警告核心是最后一句Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x14c689450 YLBtnView:0x147a5bc00.width == YLBtnView:0x147a5bc00.height * 8.33333>,YLBtnView设置了固定高度45,宽度与父视图相等,又设置了宽高比例,导致宽度*8.33333不等于45而冲突,系统只能打破其中一个关键约束(YLBtnView:0x147a5bc00.width == YLBtnView:0x147a5bc00.height * 8.33333 )来 “自救”。但这里只有控件的类名和内存地址,警告多的时候很难区分具体是哪个控件,可以点击顶部菜单Debug > Pause暂停调试,在控制台输入po命令,查看具体信息:po 0x1426d2e00//查看控件详细信息,包含superview、所在ViewController,若无法输出,用下面的命令分布查找 po [0x1426d2e00 class]//查看控件类名 po [0x1426d2e00 superview]//查看控件父组件 po [0x1426d2e00 nextResponder]//查看控件所属的类,多次向外层依次执行查找,直到输出ViewController查看控件后删除多余的固定高度约束45就可以了,如果自己不会分析,直接复制警告丢给AI,AI基本都可以准确找出问题的核心,然后在自己去定位和检查控件约束。
-
也可以通过给控件设置
accessibilityIdentifier标识符来定位控件,控制台输出的内存地址会变成我们设置的标识符号,但是对层级较深需要标记很多控件的情况来说并不适用,我是很少用这个方法。 -
通过Debug View Hierarchy查看视图层级,通过底部搜索框搜索控件,选中控件,在右侧查看控件约束。平时UI布局也经常用这个方法,可以快速排查布局问题。
- 再看一个比较特殊的例子:
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. [ <NSLayoutConstraint:0x11f728a50 UIStackView:0x10c7cebc0.height == 18>, <NSLayoutConstraint:0x136032a30 UIView:0x11b18fa80.bottom == UIStackView:0x10c7cf480.bottom>, <NSLayoutConstraint:0x136033e80 UIStackView:0x10c7cf480.top == UIView:0x11b18fa80.top>, <NSLayoutConstraint:0x136030e60 UIView:0x11b18f480.bottom == UIStackView:0x11fa73100.bottom + 16>, <NSLayoutConstraint:0x136030fa0 UIStackView:0x11fa73100.top == UIView:0x11b18f480.top + 16>, <NSLayoutConstraint:0x136033480 GradientView:0x1426d2200.bottom == UIView:0x11b18f480.bottom>, <NSLayoutConstraint:0x1341e5e00 UIView:0x11b18f480.top == GradientView:0x1426d2200.top>, <NSLayoutConstraint:0x1341e6bc0 UITableViewCellContentView:0x1288539c0.bottom == GradientView:0x1426d2200.bottom>, <NSLayoutConstraint:0x1341e6d50 GradientView:0x1426d2200.top == UITableViewCellContentView:0x1288539c0.top + 10>, <NSLayoutConstraint:0x128bc2260 YLHeadView:0x1426d2e00.height == 36>, <NSLayoutConstraint:0x14264f750 UIImageView:0x1426d2800.centerY == UIStackView:0x11fa71f80.centerY>, <NSLayoutConstraint:0x12d881950 UIStackView:0x1343e2f40.top == UIStackView:0x10c7cebc0.top>, <NSLayoutConstraint:0x12d881630 UIStackView:0x1343e2f40.bottom == UIStackView:0x10c7cebc0.bottom>, <NSLayoutConstraint:0x12d880140 UIStackView:0x1343e3800.top == UILabel:0x1426d7480.top>, <NSLayoutConstraint:0x12d880000 UIStackView:0x1343e3800.bottom == UIStackView:0x1343e2f40.bottom>, <NSLayoutConstraint:0x11f728d20 UIStackView:0x10f526bc0.top == _UILayoutSpacer:0x13650a900.top>, <NSLayoutConstraint:0x11f7299f0 UIStackView:0x10f526bc0.bottom == _UILayoutSpacer:0x13650a900.bottom>, <NSLayoutConstraint:0x136033d40 UIStackView:0x10f525500.top == UILabel:0x142700700.top>, <NSLayoutConstraint:0x10f079f90 UIStackView:0x10f525500.bottom == UIStackView:0x10f526bc0.bottom>, <NSLayoutConstraint:0x1425df8e0 UIStackView:0x12fd20380.top == _UILayoutSpacer:0x11c7b9300.top>, <NSLayoutConstraint:0x1427a4640 UIStackView:0x10c7cf480.bottom == UIStackView:0x12fd201c0.bottom>, <NSLayoutConstraint:0x1425df980 UIStackView:0x12fd20380.centerY == YLHeadView:0x1426d2e00.centerY>, <NSLayoutConstraint:0x14264ccd0 UIStackView:0x12fd201c0.top == _UILayoutSpacer:0x11c7b8300.top>, <NSLayoutConstraint:0x14264d220 UIStackView:0x12fd201c0.centerY == UIStackView:0x12fd20380.centerY>, <NSLayoutConstraint:0x14264d360 UIStackView:0x10c7cf480.top == UIStackView:0x10f525500.top>, <NSLayoutConstraint:0x14264dc70 UIStackView:0x11fa71f80.top == UIStackView:0x1343e3800.top>, <NSLayoutConstraint:0x14264dcc0 UIStackView:0x11fa71f80.bottom == UIView:0x11b18fa80.bottom>, <NSLayoutConstraint:0x14264f390 UIStackView:0x11fa73100.top == _UILayoutSpacer:0x11c7b9500.top>, <NSLayoutConstraint:0x14264f5c0 UIStackView:0x11fa73100.centerY == UIImageView:0x1426d2800.centerY>, <NSLayoutConstraint:0x12d880500 UIStackView:0x1343e2f40.top >= UILabel:0x1426d7480.bottom + 8>, <NSLayoutConstraint:0x1342a13b0 UIStackView:0x10f526bc0.top == UILabel:0x142700a80.bottom + 4>, <NSLayoutConstraint:0x1342a1400 UILabel:0x142700a80.top == UILabel:0x142700700.bottom + 4>, <NSLayoutConstraint:0x1427a7bb0 UIStackView:0x12fd201c0.top >= UIStackView:0x10f525500.bottom + 10>, <NSLayoutConstraint:0x14264de00 UIView:0x11b18fa80.top >= UIStackView:0x1343e3800.bottom + 7>, <NSLayoutConstraint:0x1425df7a0 _UILayoutSpacer:0x11c7b9300.top <= YLHeadView:0x1426d2e00.top>, <NSLayoutConstraint:0x14264cb90 _UILayoutSpacer:0x11c7b8300.top <= UIStackView:0x12fd20380.top>, <NSLayoutConstraint:0x14264f2a0 _UILayoutSpacer:0x11c7b9500.top <= UIStackView:0x11fa71f80.top>, <NSLayoutConstraint:0x1425f3160 UITableViewCellContentView:0x1288539c0.height == 100> ] Will attempt to recover by breaking constraint <NSLayoutConstraint:0x128bc2260 YLHeadView:0x1426d2e00.height == 36>同样,主要看最后一句Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x128bc2260 YLHeadView:0x1426d2e00.height == 36>,这个例子的核心是UITableViewCell 的 ContentView 设置了固定高度100,但内部嵌套的多层视图(StackView、Button、YLButton 等)通过一系列约束计算出的总高度,和这个固定的 100 高度无法兼容,系统只能打破其中一个关键约束(YLHeadView:0x1426d2e00 的高度30 )来 “自救”。但我找到控件后检查发现我的约束没有问题,也没有任何地方设置cell的contentView高度为100,只设置了一个预估高度如下:_tableView.rowHeight = UITableViewAutomaticDimension; _tableView.estimatedRowHeight = 100;那为什么预估高度会变成固定高度呢,主要原因有3种:
-
约束缺失,没有形成 “自下而上” 的自适应约束链(这里我的例子是没有缺失的)。
-
约束多余,stackview中设置了多余的约束,过多的固定宽高等让系统放弃了自动计算(这里我也是没有的)。
-
xib默认的高度和代码设置的预估高度相差太多,这里我的xib默认高度是420,但代码设置是100,系统检测到相差太大放弃了自动计算将预估高度视为固定高度导致异常。修改预估高度为420后正常了。
-
730

被折叠的 条评论
为什么被折叠?



