Gallery[39367:a0b] 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:0xc1a1e80 V:[UIView:0xc1a2b10(284)]>", "<NSLayoutConstraint:0xc1a36c0 V:[_UILayoutGuide:0xc1a2d20]-(65)-[UIView:0xc1a2b10]>", "<NSLayoutConstraint:0xc1a36f0 V:[UIView:0xc1a2b10]-(199)-[_UILayoutGuide:0xc1a3230]>", "<_UILayoutSupportConstraint:0xc15dbd0 V:[_UILayoutGuide:0xc1a2d20(20)]>", "<_UILayoutSupportConstraint:0xc1a1510 V:|-(0)-[_UILayoutGuide:0xc1a2d20] (Names: '|':UIView:0xc1a2930 )>", "<_UILayoutSupportConstraint:0xc1a3720 V:[_UILayoutGuide:0xc1a3230(0)]>", "<_UILayoutSupportConstraint:0xc1a30e0 _UILayoutGuide:0xc1a3230.bottom == UIView:0xc1a2930.bottom>", "<NSAutoresizingMaskLayoutConstraint:0x8c6c6a0 h=--& v=--& H:[UIView:0xc1a2930(320)]>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0xc1a1e80 V:[UIView:0xc1a2b10(284)]> Break on objc_exception_throw to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful. . . . |
你还记得我说过“必须有足够的约束,自动布局才能计算所有视图的位置和尺寸”么?好吧,在这个例子中,约束又太多了。当你看到“不能同时满足所有约束(Unable to simultaneously satisfy constraints)”的错误的时候,那就意味着你的约束之间有冲突。
让我们再看一下哪些约束:
绿色视图上设置了6个约束,4个间距约束(1-4)和你刚刚设置的宽高约束(5和6)。冲突在哪呢?
在竖屏的时候不应该有问题,因为在数学上是满足的。父视图的宽是320点。如果你添加了Horizontal Space和Width约束,你应该保证它们的总长度小于等于320点。这是我计算视图位置的方式:98+160+62 = 320。同样的,所有竖向约束相加以后就应该是568点。
但是当你旋转设备为横屏时,窗口(指的父视图)就变成了568点宽。这意味着98+160+62+? = 568。这里额外多了248点,无法满足方程式,所以自动布局不知道从哪里来得到这248点。同样的,对于竖向约束也是一样。
解决冲突的办法有两个:第一是保持view的宽度固定,但外边距必须可变。第二是保持外边距固定,但宽度必须可变。你不能同时固定它们。你要去掉这些约束中的其中一个。在上面的例子中,若你想要在横屏竖屏中都拥有同样的宽,那Horizontal Space就必须去掉。
移除右边的横向间距和下边竖向间距。故事板上会显示成这样:
好了,现在view就有一个正确约束来预判它的尺寸与位置了 - 不多,不少。运行app来查证一下错误信息是否已经没有了,然后这个view在旋转以后是否能保持宽度。
注意:尽管Interface Builder可以很好的警告你有无效的约束,但它不是神。它只会警告你:噢,你的约束不够。但是如果你约束太多了,它就无法检测出来了。不过至少在出错的时候,自动布局还是会给出一个详细的错误信息。如果你想要学习更多关于如何分析错误信息和诊断布局问题,那你可以看 iOS 6 by Tutorials中的“Intermediate Auto Layout”。
竖屏绘制
拖拽一个label到绿色的view上。注意看,现在参考线出现在了绿色view的内部,因为它是label的父视图。
调整label的位置到参考线的下边距,横向居中的地方。给label下面添加与绿色view之间的间距约束,有20点的距离。快速的方式是使用Pin按钮,仅选择下面的T字架:
现在给label添加横向居中的约束。你已经试过Editor\Align菜单了,但你也可以使用自动布局菜单的Align按钮。选择label并点击Align按钮,得到一个弹出窗:
在Horizontal Center前打上勾然后点击Add 1 Constraint。这时候,故事板应该是这个样子的:
注意这两个新的横向和纵向间距约束是位于绿色view自己的约束列表里,而不是在主视图里。
拖拽一个新的Image View对象到故事板上,让你的布局看起来像这样::
这个图片视图固定了上,左,右边缘在父视图上,但下部以标准的8点间距连接在了label的顶部。如果你不确定要怎么做,那就跟着下面的步骤走:
1. 拖拽image view到绿色视图中,现在不用担心它的尺寸和位置:
2. 选中image view,按Pin按钮选择下面的选项:
上,左,右的T字架设置为20点,但是下面的设置为8点。重点:对于Update Frames,你应该选择Items of New Constraints。如果你左边距已经默认满足要求,故事板看起来就是这样的:
上面这个约束是你选择了一个不一样的frame。如果你选择了Items of New Constraints, Interface Builder将自动的调整画面来添加约束,一切看起来都很棒:
当然,如果你选错了frame,你也可以使用Resolve Auto Layout Issues button来修复它:
下载这个教程资源并解压这个文件。你就会找到一个图片文件夹 - 添加这个文件夹到你的项目。设置Ray.png作为这个image view的图片,改变image view的模式为Aspect Fit并且设置它的背景色为白色。把label的文本改为“Ray”。
你的布局应该是这样的:
你可能注意到绿色view内部的约束突然变成了橘黄色。这发生在你给image view设置图片的时候。你的布局为什么突然无效了?幸运的是你可以带着你的猜想来让Xcode告诉你为什么错处了。
点击这个这个红色的靶子,它位于View控制器的Outline文档中:
你会看到一个Content Priority Ambiguity(内容优先级歧义)的错误。这意味着:如果image view和label都没有固定的高度,那自动布局系统就不知道应该怎么分配大小。(Interface Builder似乎会忽略已经设置过的固定高度约束)
让我们把绿色的view的高度变为100点试试。自动布局是怎么将这100点分配给view内部的label和image view的?是label保持尺寸而image view变成100点高了么?还是label变高了而image view保持尺寸?还是他们各自被分配了50点,又或者划分成25/75,40/60,又或者其它的什么结合?
如果你不解决这个问题,那么自动布局系统就会去猜测,就可能导致结果莫名其妙。
恰当的解决办法是改变label的“Content Compression Resistance Priority”。你将从随后内容中学习到更多。现在,打开label的Size inspector,设置Content Compression Resistance Priority的vertical为751。这样就使它的优先级高于image view。然后继续设置Content Hugging Priority为252。
这时候T字架就会再次变成蓝色,自动布局系统的警告也会消失。
添加到另一个顶端
拖拽绿色的view到主视图的左上角。回忆一下之前做过的,绿色的view已经有Horizontal Space和Vertical Space约束来控制它在父视图中的位置了。现在,这些约束依然存在,它们导致了视图的frame无法对期喎�"http://www.it165.net/design/wrss/" target="_blank" class="keylink">rss6/vM/foaM8L3A+CjxwPjxpbWcgc3R5bGU9"display: block; margin-left: auto; margin-right: auto;" src="http://www.it165.net/uploadfile/files/2014/0920/2014092019054458.png" alt="" />
为了修正它,使用Resolve Auto Layout Issues按钮并选择Update Constraints。之前你使用的Update Frames,用来移动和重定义view的尺寸来匹配你的约束。现在这刚好是一个相反的操作:你会用你的约束来匹配view的frame。
你可能注意到在顶部的Vertical Space现在是负的。这种情况是因为约束链接到了Top Layout Guide(顶部参考线)。不要问为什么,没有规定说约束值不能是负的,你可以就像这样留下它。(如果你看着碍眼,那就删掉 “Vertical Space (-20)”约束,然后把view重新绑定到窗口的顶部)
Horizontal Space现在为0,表现为一条紧贴窗口左边缘的粗蓝线。尽管view已经位于角落里了,但是它任然需要约束来固定住它:
选择绿色的视图按下?D来复制它。移动复制的视图到右上角:
注意T字架现在是橘黄色的。当你复制它的时候,它已经上丢失了X,Y坐标的约束。为了修正它,固定view到窗口的右、上边缘。
再复制两次,分别把复制的视图放在坐下角落和右下角落。然后再把他们固定在他们该在地方。
改变后的场景如下:
哈哈~他们看起来好像都是程序员 :-)
运行app,在竖屏上很棒,但是横屏上可能不是那么好:
本来应该很棒的战士却变糟了:你给4个色彩鲜艳的容器view设定了固定的宽和高,因此它们就总是保证这样的尺寸,而不管它们的父视图的尺寸如何。
从这4个view下,选择Width (160)和Height (284)的约束并删除它们(在Outline文档中很容易完成)。如果你现在运行app,你会看到:
注意:如果你很奇怪为什么有的view变得比其他的大了,那我告诉你,这是固有内容尺寸的原因。图片的尺寸决定了image view有多大。文本的尺寸决定了label有多大。再加上四边的20点外边距,所有的尺寸加起来决定了每个视图的尺寸。
这看起来很像你的上一部分解决的问题,因此你要向前思考,你可能回忆起来了,你要让每个view的宽和高都相等。
选择这4个view。Outline大纲里很容易完成;按住?点击这4个view。你就可以添加这个约束。在弹出窗中,给Equal Widths和Equal Heights打上勾,然后点击Add 6 Constraints。
再次运行这个app并且旋转设备。噢...还是不咋地:
所有的view都有同样的高,并且他们也有同样的宽,你的约束是对的。但这个宽和高可能不是你想要的。
光告诉自动布局系统必须有同样的尺寸是不够的。因为自动布局系统不炸ky"http://www.it165.net/qq/" target="_blank" class="keylink">qq1wNXiNLj2dmlld8rHu6XP4MGsvdO1xKGjy/zDx7XEsd/M+bHftcTJ6LzGo6y1q9Tay/zDx9auvOTDu9PQ1eLR+bXE1LzK+KGj19S2r7K8vta+zbK71qq1wMv80OjSqtTaJmxkcXVvO1JheSZyZHF1bzu49iZsZHF1bztNYXR0aGlqcyZyZHF1bzvWrrzku6631rSwv9qhozwvcD4KPHA+yOe5+9fUtq+yvL7Wz7XNs9fUvLqyu8TczeqzyaOsxMfE477N0qq45svfy/yhozwvcD4KPHA+PGltZyBzdHlsZT0="display: block; margin-left: auto; margin-right: auto;" src="http://www.it165.net/uploadfile/files/2014/0920/2014092019054675.png" alt="" />
选择Ray和Matthijs的视图,从编辑菜单选择Pin\Horizontal Spacing。因为view之间是边贴边的,那在它们之间就要加上尺寸为0的Horizontal Space约束。好了,现在有足够的约束让自动布局系统了解两个view之间的关系了。再给Ray和Dennis Ritchie视图之间使用Editor\Pin\Vertical Spacing。
再运行app,现在看起来就像这样:
注意:Interface Builder任然会抱怨view的位置不对。我不确信为什么会发生这种情况,可能是Xcode有bug吧。如果这些警告信息让你不爽,那就选择主视图(或view控制器)并且从Resolve Auto Layout Issues菜单选择Update All Frames in View Controller。不过你的app运行时这种改变不会起作用,但至少让Xcode看起来很舒心。
image view上有一点要注意:它们可能会延伸了,因为你没有给它们一个固定尺寸。你可能不知道,这是有意这样做的。image view不能适配横屏的模式。如果你想保持image view的原始宽高比,那很不幸。在Interface Builder中你是无法得到下面这样的效果的:
不幸的是,Interface Builder不能正确的提供用约束保持view的原样宽高比。如果要这么做,你就需要自己编程。你可以在iOS 6 by Tutorials的“Intermediate Auto Layout”中学习如何操作。
我该去哪继续学习?
如果你全都看完做完了,祝贺你 - 你现在知道自动布局是什么了,并且有了一定的实践基础!但你任然有许多需要学习...
这个教程的第一部分你可以从iOS 6 by Tutorials一书中的自动布局章节阅读到。第二部分会教你如何使用自动布局来创建更多“真实世界”的场景布局,你可以从Interface Builder学习一切你想知道的自动布局的知识。
但是就像其他的视觉设计工具一样,Interface Builder也有自己的局限性。有时,它只能通过NSLayoutConstraint对象来从代码实现。IOS 6的教程有一个章节包含了这个主题所有的内容,Intermediate Auto Layout。因此,如果你想知道自动布局的后半部分,请买这本书吧!(翻译到最后,发现居然带广告,为了尊重原作者,还是给出连接吧~~~)