Beginning Auto Layout Tutorial in iOS 7: Part 6

Gallery example

The Gallery app

屏幕有四个分开的相同的矩形,每个矩形有一个label和一个image view。创建一个Gallery的项目。在Main.storyboard中,拖拉一个view大小为160 by 284 points。

Green view in canvas

Note: There are two main reasons why you would drop a plain UIView onto a storyboard:

a) You’re going to use it as a container for other views, which helps with organizing the content of your scenes; or

b) It is a placeholder for a custom view or control, and you will also set its Class attribute to the name of your own UIView or UIControl subclass.

现在设置一些约束。你已经知道了两种约束,使用Editor\Pin和Align菜单。或者ctrl+dragging控件来实现。然后还有一种方式是使用下面的工具选项。

Auto Layout buttons

Align, Pin, Resolve Auto Layout Issues, and Resizing Behavior从左到右依次是对齐,固定,解决auto layout问题,重定义大小。

前三个选项顶部的菜单栏目中都一样的,resizing behavior允许你当resize view的时候改变约束

The pin popup

选择pin弹出如上的筐体。然后点击四个T-bar颜色会变成solid red,最后选择add 4 constrains选项就会产生新的约束。

Spacing to nearest neighbor

 

View with the four new constraints

选择Assistant editor中的priview也可以先预览模拟器竖屏和横屏的效果图

Storyboard preview

Note: Maybe you wondered why the constraint at the top of the view didn’t go all the way up to the top of the screen:

Space at top

Instead it stops at the status bar. But in iOS 7 the status bar is always drawn on top of the view controller — it is no longer a separate bar — so what gives? When you created the constraint it didn’t actually attach to the top of the screen but to an invisible line called the Top Layout Guide.

On a regular view controller this guide sits at 20 points from the top of the screen, at least when the status bar is not hidden. In a navigation controller it sits below the navigation bar. Because the navigation bar has a different height in landscape, the Top Layout Guide moves with the bar when the device is rotated. That makes it easy to place views relative to the navigation bar. There is also a Bottom Layout Guide that is used for the tab bar and toolbars.

为了不让模拟器旋转后宽度和高度发生变化,那么你可以设置宽度和高度

Pin popup with width and height selected

运行app,报错误

2015-01-04 10:25:58.691 Gallery[888:279956] 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:0x7f8adae04f50 H:[UIView:0x7f8adac0b950(160)]>",
    "<NSLayoutConstraint:0x7f8adae08360 UIView:0x7f8adac0b950.leading == UIView:0x7f8adae05d80.leadingMargin + 204>",
    "<NSLayoutConstraint:0x7f8adae083b0 UIView:0x7f8adae05d80.trailingMargin == UIView:0x7f8adac0b950.trailing + 204>",
    "<NSLayoutConstraint:0x7f8adeb6c870 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7f8adae05d80(375)]>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f8adae04f50 H:[UIView:0x7f8adac0b950(160)]>

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.
2015-01-04 10:25:58.693 Gallery[888:279956] 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:0x7f8adae08360 UIView:0x7f8adac0b950.leading == UIView:0x7f8adae05d80.leadingMargin + 204>",
    "<NSLayoutConstraint:0x7f8adae083b0 UIView:0x7f8adae05d80.trailingMargin == UIView:0x7f8adac0b950.trailing + 204>",
    "<NSLayoutConstraint:0x7f8adeb6c870 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7f8adae05d80(375)]>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f8adae083b0 UIView:0x7f8adae05d80.trailingMargin == UIView:0x7f8adac0b950.trailing + 204>

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.
2015-01-04 10:25:58.693 Gallery[888:279956] 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:0x7f8adae05be0 V:[UIView:0x7f8adac0b950(284)]>",
    "<NSLayoutConstraint:0x7f8adae08400 V:[_UILayoutGuide:0x7f8adae06db0]-(81)-[UIView:0x7f8adac0b950]>",
    "<NSLayoutConstraint:0x7f8adae08450 V:[UIView:0x7f8adac0b950]-(215)-[_UILayoutGuide:0x7f8adae07ca0]>",
    "<_UILayoutSupportConstraint:0x7f8adae080f0 V:[_UILayoutGuide:0x7f8adae06db0(20)]>",
    "<_UILayoutSupportConstraint:0x7f8adae08290 V:|-(0)-[_UILayoutGuide:0x7f8adae06db0]   (Names: '|':UIView:0x7f8adae05d80 )>",
    "<_UILayoutSupportConstraint:0x7f8adae08ab0 V:[_UILayoutGuide:0x7f8adae07ca0(0)]>",
    "<_UILayoutSupportConstraint:0x7f8adae08150 _UILayoutGuide:0x7f8adae07ca0.bottom == UIView:0x7f8adae05d80.bottom>",
    "<NSLayoutConstraint:0x7f8adeb6c8c0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7f8adae05d80(667)]>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f8adae05be0 V:[UIView:0x7f8adac0b950(284)]>

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.

还记得之前说的必须设置足够的约束来使得auto layout知道如何布局?而现在的情况是设置的约束太多了。有冲突的地方。一旦报错 “Unable to simultaneously satisfy constraints”, 就是说明你设置的约束太多了有冲突。让我们来看看怎么回事。

Conflicting constraints

 

Width and height constraints on green view

可以看到屏幕中设置了6个约束,四个之前的约束1-4然后还有新的宽度和高度约束可以查看5和6.那么冲突在哪里呢?在竖直的模式下不应该有问题因为数学运算。superview的宽度是320 points。如果你添加水平距离长度和宽度约束,然后你应该最终得到320. 计算原则是98+160+62 = 320 (就是左边筐到view左边框的宽度+view的宽度+view右边筐到superview右边筐的距离)同样的垂直的约束应该是568 。但是当你旋转设备到横屏的状态时,窗口是568 points宽度。意思是98+160+62+?= 568.有248额外的points auto layout无法得知如何获得。
冲突在于:要么view的宽度确定并且其中一个margins必须可变或者margins确定并且width可变。你不能同时将他们的值都设定死。所以其中一个约束需要删除。在上面的例子中,你想让view的宽度和高度在旋转时候都保持不变那么trailing 水平距离就必须要删除掉。删除右侧的horizontal space和底部的vertical space。

Conflicting constraints fixed

再次运行app正常了。

Painting the portraits

添加一个新的Label到view中去,你可以注意到guides出现了,因为它将是label的superview

Dragging the label

设置label的约束,水平居中布局

Pin menu with bottom T-bar selected

拖拉Image View进去

Image view with constraints

1. Drag the image view into the green view but don’t worry too much about its size or position:

2. With the image view selected, press the Pin button and choose the following options:

Pin menu for image view

一定要选择Items of New Constraints给update frames选项,不要是默认的,否则auto layout会显示成如下的样子,因为你设定的frame和默认的imageview的frame有冲突所以不对。

Misplaced image view

当然你也可以用如下的方式来修改成这个效果Resolve Auto Layout Issues

Resolve issues menu

Download the resources for this tutorial 下载图片资源文件,设置Ray.png 把背景设置成白色然后把图片的适应方式设置成Aspect Fit

Gallery with Ray

可以发现在绿色view中的约束变成了橘色。当你设置image view的时候会出现这个问题。那么你的layout怎么会突然失效了呢?幸运的是xcode可以告诉你哪里错了。

Content priority ambiguity error

有一个content priority ambiguity的错误。意思是:image view和label没有固定的高度。auto layout不知道scale多少如果green view改变高度的话。(interface builder似乎忽略这点green view实际上有一个固定的高度约束)

比如说你的green view高了100 points。那么auto layout如何在label和image view间分配这新的100 points呢?当label 还是之前的大小image view会增高100 points吗?或者当image view还是之前的大小label会增高100 points吗?他们都有额外的50 points还是他会分隔成25/75, 40/60或者其他可能的划分呢?

如果你不解决这个问题那么auto layout就必须猜并且结果可能是出乎预料的。

合适的解决方案是改变label的content compression resistance priority。选择label的size inspector设置vertical content compression resistance priority为751.这样label的priority就会高于image view的。设置content hugging priority为252.这两个数都是报错时候显示的建议数字。imageview的默认的夜市750,251,这样就高于imageview了

Compression resistance priority

警告没有了

Adding the other heads

把green view拖拉到左上角。使用Resolve Auto Layout IssuesUpdate Constraints.delete that “Vertical Space (-20)” constraint

Misaligned green view

选择⌘D来复制一个同样的并且移动到右上角,出现了橘色,设置top和trailing to right就可以了

Duplicate view in top-right corner

同样的复制两个再分别pin到左下和右下方

Gallery with all 4 heads

但是横屏的话却是有问题的

Gallery landscape bad

之所以这样是因为你设置了固定的宽度和高度,因此他们总是这样的高度和宽度而不会虽然superview的高度和宽度的变化而变化

选择Width (160) and Height (284)并且删除,再次运行就会是如下的样子:

Still bad in landscape

Note: If you’re wondering why some of the views are larger than others, this is again related to the intrinsic content size. The size of the image determines how large the image view is; the size of the text determines how large the label is. Taken together with the constraints for the margins — 20 points on all sides — this determines the total size of each view.

在document line中使用选择所有的有颜色的view,你可以一次添加所有的约束。在pin中选择equal widths and equal heights并且选择add 6 constraints。再次运行还是不对,但是你会发现所有的view宽度和高度都一样了。

Add equal widths and heights constraints

 

Gallery landscape equal sizes

这是因为他们之间没有约束,auto layout不清楚如何布局他们。

选择ray和matthijs并且选择Pin\Horizontal Spacing.因为他们是side by side,这样添加一个他们之间的horizontal space 0的约束并且足以让auto layout知道这两个view是有关联的。并且在ray和dennis之间添加一个Editor\Pin\Vertical Spacing

Run the app again, and this time it looks all right:

Gallery landscape OK

 

posted @ 2015-01-04 15:57  如来藏  阅读(345)  评论(0编辑  收藏  举报