iOS Programming Introduction to Auto Layout 自动布局
A single application that runs natively on both the iPad and the iPhone is called a universal application.
一个原生的能运行在iPad 和iPhone 的应用叫做universal application
Then select the Homepwner target in the project and targets list and the General tab. This tab presents a convenient interface for editing some of the target's properties.
The table view interface knew how to resize itself for the iPad sized-screen, but your custom detail interface needs some guidance. You will provide that guidance using Auto Layout.
你的table view interface 知道如何适应iPad 的屏幕尺寸,但是你自己的detail interface 需要一些引导了。你需要使用自动布局来引导你的应用。
1 The Auto Layout System 自动布局系统
you have defined the frames of your views with absolute coordinates either programmatically or by configuring them in Interface Builder.
到目前为止,你定义了你的view 的frame 通过绝对的坐标系通过编程或者通过Interface Builder .
Absolute coordinates, however, make your layout fragile because they assume that you know the size of the screen ahead of time.
绝对布局,容易让你的布局混乱,因为他们假定你已经知道了屏幕的尺寸。
Using Auto Layout, you can describe the layout of your views in a relative way that allows the frames to be determined at runtime so that the frames' definitions can take into account the screen size of the device that the application is running on.
使用auto layout ,你能表述用一种相对的方式布局你的视图 ,允许frame 在运行时被决定。这样你的应用就能够根据你运行的设备来考虑屏幕的尺寸了。
Remember that points are used when laying out your interface and map to physical pixels on the device's screen. A retina device has the same size screen in points as a non-retina device, even though it has twice as many pixels.
point not pixels .
2 Alignment rectangle and layout attributes
The Auto Layout system works with yet another rectangle for a view – the alignment rectangle.
auto layout system 仍然与另一个rectangle 一起为view 工作— alignment rectangle.
This
rectangle is defined by several layout attributes
这个矩形被许多layout attribute属性定义。
(1)Width/Height:These values determine the alignment rectangle's size.
这个值决定了alignment rectangle的尺寸。
(2)Top/Bottom/Left/Right These values determine the spacing between the given edge of the alignment rectangle and the alignment rectangle of another view in the hierarchy.
这个值决定了在给定的alignment rectangle 和在hierarchy中得另外一个view的alignment rectangle 的alignment rectangle .
(3)CenterX/CenterY :These values determine the center point of the alignment rectangle.
这些值决定了这个alignment rectangle 的中心位置。
(4)Baseline:This value is the same as the bottom attribute for most, but not all, views.
这个值经常和bottom attribute 相同,但不总是。
For example, UITextField defines its baseline to be the bottom of the text it displays rather than the bottom of the alignment rectangle.
比如说,UITextField 定义它的baseline 在text 的底部,而不是在alignment rectangle的底部。
This keeps "descenders" (letters like 'g' and 'p' that descend below the baseline) from being obscured by a view right below the text field.
(5) Leading/Trailing:These values are used with text-based views like UITextField and UILabel.
这个值用在像UITextField 和UILabel 的text-based views中。
If the language of the device is set to a language that reads left-to-right (e.g., English),then the leading attribute is the same as the left attribute and the trailing attribute is the same as the right attribute.
如果一个设备的语言设置为从左到右,那么leading attribute 和left attribute 相同,trailing attribute 和right attribute 相同。
If the language reads right-to-left (e.g., Arabic), then the leading attribute is on the right and the trailing attribute is on the left.
如果一个语言从右到左,那么它的leading attribute 是在右边,而trailing attribute 是在左边。
By default, every view in a XIB file has an alignment rectangle, and every view hierarchy uses Auto Layout.
默认情况下,每个在XIB中的view 都有一个alignment rectangle,每个view 都用auto layout.
You do not define a view's alignment rectangle directly. You do not have enough information (screen size!) to do that.
你不能直接定义一个view的alignment rectangle。你并没有足够的信息(屏幕的尺寸)做这个。
Instead, you provide a set of constraints. Taken together, these constraints allow the system to determine the layout attributes, and thus the alignment rectangle, for each view in the view hierarchy.
取而代之的是你提供了一系列的constraints。 这些constraints 放在一块,将允许系统决定layout attributes,因此也有为在view hierarchy中每个view的alignment rectangle 。
3. constrants
A constraint defines a specific relationship in a view hierarchy that can be used to determine a layout attribute for one or more views.
constranit定义了view hierarchy的一个特殊的关系。它能够用来为一个或多个view决定一个layout attribute 属性。
For example, you might add a constraint like "the vertical space between these two views should always be 8 points" or "these views must always have the same width."
比如:你可以添加这样一个限制: "这两个view的垂直空隙应该总是8 points"或者"这些views 总是需要同样的宽度。"
A constraint can also be used to give a view a fixed size, like "this view's height should always be 44 points."
你也可以定义这样的:"这个view 的高度应该永远是44points"。
You do not need to have a constraint for every layout attribute.
你不需要为每个layout 属性都设置一个限制。
Some values may come directly from a constraint; others will be computed by the values of related layout attributes.
一些值从一个constraint 直接给出,而另外一些可能通过相关的layout attributes 计算得到。
For example, if a view's constraints set its left edge and its width, then the right edge is already determined (left edge + width = right edge, always).
After all of the constraints have been considered, there is still an ambiguous or missing value for a layout attribute, then there will be errors and warnings from Auto Layout and your interface will not look as you expect on all devices.
在所有的constraints 已经被考虑了,但是仍然有一个模糊或遗失的值为一个layout attribute ,那么将会有自动布局或界面错误或警告。
Debugging these problems is important。
Let's see how using the view in the BNRDetailViewController's view hierarchy that will be simple to constrain – the toolbar.
让我们看看BNRDetailViewController's 的view hierarchy,来限制toolbar。
describe what you want the view to look like regardless of screen size. For the toolbar, you could describe it like this:
(1)The toolbar should sit at the bottom of the screen. 在底部
(2)The toolbar should be as wide as the screen. 和屏幕一样宽
(3)The toolbar's height should be 44 points. (This is Apple's standard for instances of UIToolbar.)高度应该是44
To turn this description into constraints in Interface Builder, it will help to understand how to find a view's nearest neighbor.
把这些描述转化成Interface Builder,它将帮助你理解怎样找到一个view 的最近的neighbor。
The nearest neighbor is the closest sibling view in the specified direction (Figure 15.6).
一个最近的neighbor 是最近的sibling view 在指定的方向上。
If a view does not have any siblings in the specified direction, then the nearest neighbor is its superview, also known as its container.
如果一个view在指定的方向上sibling,那么它的nearest neighbor 是它的sup view,也就是它的container。
Now you can spell out the constraints for the toolbar:
1. The toolbar's bottom edge should be 0 points away from its nearest neighbor (which is its container – the view of the BNRDetailViewController).
2. The toolbar's left edge should be 0 points away from its nearest neighbor.
3. The toolbar's right edge should be 0 points away from its nearest neighbor.
4. The toolbar's height should be 44 points.
Constraints can be added using Interface Builder or in code.
Constraints可以通过Interface Builder或代码添加上。
4. Adding Constraints in Interface Builder
At the bottom righthand corner of the canvas, find the Auto Layout
constraint menu
To turn these values into constraints, click the orange struts separating the values from the square in the middle. The struts will become solid lines.
Find the Constraints section and reveal its contents. However, you will see only three constraints here. The fourth constraint, the fixed height of the toolbar, is in a separate Constraints section underneath Toolbar.
Find the Constraints section and reveal its contents. However, you will see only three constraints here. The fourth constraint, the fixed height of the toolbar, is in a separate Constraints section underneath Toolbar.
Why the division? Once a constraint is created, it is added to a particular view object in the view hierarchy.
为什么会有这样的区分?一旦constraint 被创建,它被添加到在一个view hierarchy 的一个专门的view object中。
Which view gets a constraint is based on which views that constraint affects.
哪一个view获得这个限制依赖于constraint 影响了哪个view。
The three edge constraints are added to the Control because they apply to both the toolbar and its superview, the view of the BNRDetailViewController.
The height constraint, on the other hand, is added to the toolbar because it applies only to the toolbar.
If you select any of these constraints in the dock, a blue line will appear on the canvas representing the constraint. (Some constraints will be harder to see than others.) Selecting the view will show you all the constraints influencing that view.
5.Adding more constraints
Notice that the lines representing the constraints on the text field are orange instead of blue.
This color difference means that the text field does not have enough constraints for Auto Layout to unambiguously specify its alignment rectangle.
如果这个颜色不同意味着text field 没有足够的限制来布局来指明它的alignment rectangle。
You can align two or more views according to any layout attribute. Here the best choice is to align baselines.
你可以align两个或多个view根据任何一个layout attribute.
On the canvas, select the text field and hold the Shift key down to select the Name label at the same time.
在canvas,选择text field 并按住shift 键选择Name label ,同时。
From the constraints menu, click the icon to reveal the Align menu. Check the box next to
Baselines and add 1 constraint.
You can also add constraints by Control-dragging on the canvas.
This dragging is similar to setting up outlets and actions. You drag from one view to another.
你的drag 从一个view到另一个。
After you release the mouse button, you get a list of constraints that you can add.
当你释放mouse button,你获得了一列你添加的限制。
Control-drag to the Name label again. This time, select Left from the menu. This is identical to opening the Align menu and checking Leading Edges.
Finally, you need to fix the label's height and width at their current values. Because these constraints affect only the Serial label, you do not Control-drag to another view. Instead, make a very short diagonal Control-drag within the Serial label.
6. Priorities
Each constraint has a priority level that is used to determine which constraint wins when more than one constraint conflicts.
每一个constraint 都有一个优先级,当多余一个限制的冲突发生时,决定哪个constraint 胜利。
A priority is a value from 1 to 1000, where 1000 is a required constraint. By default, constraints are required, so all of the constraints that you have added are required. This means that the priority level would not help if you had conflicting constraints.
一个优先级是从1到1000的数值,而1000是必要的constraint.默认情况下,限制是必要的。所以所有你的constraints is required .这也就意味着priority level 不能帮助你。
Instead, Auto Layout would report an issue regarding unsatisfiable constraints.
auto layout 如果遇到不满意的constraints 就会报告一个问题。
Typically, you find the constraints that conflict and then either remove one or reduce the priority level of a constraint to resolve the conflict but keep all the constraints in play.
一般情况下,你会找到这个冲突,然后删除一个,或者降低一个constraint 的优先级,但保留所有,来解决。
7Debugging Constraints
7.1 Ambiguous layout
An ambiguous layout occurs when there is more then one way to fulfill a set of constraints. Typically, this means that you are missing at least one constraint.
一个ambiguous layout 发生当多于一种方式填充限制集合。一般的,这就意味着你至少缺失一个constraint.
Add two labels to the UIControl under the dateLabel and position them next to one another.
在UIControl中添加两个labels.
In the attributes inspector, change the background color of the labels to light gray so that you can see their frames.
把他们的背景颜色设置为light gray.
Now let's add some constraints to both labels. Hold down the Shift key and select both labels. Open the Pin Auto Layout menu, select the top, left, and right struts at the top, and then click Add 5 Constraints
These labels do not have enough constraints to unambiguously define their frames.
这些labels 没有足够的constraints 来明确地定义他们的frame。
Auto Layout takes its best guess at runtime, and it is not what you wanted on the iPad.
Auto Layout 在运行时采取最好的guess,然而这并不是你想要的。
You are going to use two UIView methods, hasAmbiguousLayout and exerciseAmbiguousLayout, to debug this situation.
你需要两个方法:hasAmbiguousLayout和exerciseAmbiguousLayout来debug这个情况。
Open BNRDetailViewController.m. Override the method viewDidLayoutSubviews to check if any of its subviews has an ambiguous layout.
- (void)viewDidLayoutSubviews
{
for (UIView *subview in self.view.subviews) {
if([subview hasAmbiguousLayout]) NSLog(@"AMBIGUOUS: %@", subview);
} }
一直有个疑问:在view controller里面的view的方法就可以写在View Controller里面吗?
viewDidLayoutSubviews gets called any time the view changes in size (for example, when the device is rotated) or when it is first presented on the screen.
当View 改变尺寸或它的第一次在屏幕上出现时viewDidLayoutSubviews就会被调用。
You can go a step further to actually see the other way this layout might appear. In BNRDetailViewController.m, edit the backgroundTapped: method to send the message exerciseAmbiguityInLayout to any ambiguous views.
你可以看到另一个layout。编辑backgroundTapped发送这个消息:exerciseAmbiguityInLayout到任何模糊的views。
- (IBAction)backgroundTapped:(id)sender
{
[self.view endEditing:YES];
for (UIView *subview in self.view.subviews) { if ([subview hasAmbiguousLayout]) {
[subview exerciseAmbiguityInLayout]; }
} }
Neither of the widths of the labels has been constrained, and so there is more than one solution to the system of Auto Layout equations. Because of this, there is an ambiguous layout and tapping the background switches between the two possible solutions.
Due to the other constraints you have specified, as long as one of the labels has its width constrained, the other label's width can be determined. You will get rid of the ambiguous layout by giving the two labels equal widths.
只要其中一个label有一个固定的宽度,你就能确定了。你就能去除掉ambiguous layout。
这两个效果是一样的。
In BNRDetailViewController.xib, Control-drag from one label to the other, and then select Equal Widths. Build and run the application on iPad.
The exerciseAmbiguityInLayout method is purely a debugging tool that allows Auto Layout to show you where your layouts could potentially end up. You should never leave this code in an application that you are shipping.
exerciseAmbiguityInLayout方法纯粹就是debugging方法,允许你看到你的布局潜在的展现方式。在正式代码中,不允许其出现。
7.2 Unsatisfiable constraints 不充分的限制
The problem of unsatisfiable constraints occurs when two or more constraints conflict. This often means that a view has too many constraints.
当两个或多个constraints conflict 就会出现unsatisfiable constraints .
. The label may look just as it did before, but take a look at the console.
看一下现在的控制台:
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:0x10926e650 H:[UILabel:0x10926e3e0(280)]>",
"<NSLayoutConstraint:0x109579870 H:|-(20)-[UILabel:0x10926e3e0] (Names: '|':UIControl:0x10926cc40 )>",
"<NSLayoutConstraint:0x109579910 H:[UILabel:0x10926e3e0]-(20)-| (Names: '|':UIControl:0x10926cc40 )>",
"<NSAutoresizingMaskLayoutConstraint:0x1092870b0 h=-&- v=-&- UIControl:0x10926cc40.width == _UIParallaxDimmingView:0x10957de70.width>",
"<NSAutoresizingMaskLayoutConstraint:0x109287bf0 h=--& v=--& H:[_UIParallaxDimmingView:0x10957de70(768)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x10926e650 H:[UILabel:0x10926e3e0(280)]>
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.
First, Xcode informs you that it is "unable to simultaneously satisfy constraints" and gives you some hints on what to look for.
首先Xcode 通知你"不能同时满足限制"
The console then lists all of the constraints that are related to the issue.
console紧接着列出了与此相关的所有constraints .
Finally, you are told that one of the constraints will be ignored so that the label will have a valid frame.
最后你被告知这些constraints中的一个将被忽略所以label 将会有一个有效的frame。
You can also just think through the problem. You constrained this label's leading and trailing edges to resize with the superview. Then you constrained its width to a fixed value. These are conflicting constraints, and the solution is to remove one.
delete the width constraint that you just added to the label and set its background color back to clear.
7.3Misplaced views 任意放置的view
If a view's frame in a XIB does not match its constraints, then you have a misplaced view problem.
如果一个view 的frame 并不匹配它的constraints,那么你将会有misplaced view 问题。
This means that the frame of that view at runtime will not match how it currently appears on the canvas.
也就是说那个view的frame在运行时并不和它现在展现在canvas中的一样。
Select the label that displays the date and drag it down a little bit so that the interface looks like
A rectangle with an orange, dashed border will appear where the label used to be.
一个orangle ,虚线的矩形将会出现在label原来的位置。
This is the runtime frame; at runtime, the existing constraints will position the label where this rectangle is and not where you just dragged it to.
这就是runtime frame,在runtime,已经存在的constraints 将会把label放置到orange矩形的位置,而不是你刚刚拖动的位置。
How you fix the problem depends on whether the view's size and position on the canvas are what you want.
你怎样修复这个问题取决于view的size和position在canvas是否是你想要的。
If so, then you change the constraints to work with this new position. If not, then you change the view's size or position to match the constraints.
如果是你想要的,你就改变constraints .如果不是,你可以改变view 的size或position 来匹配这些constraints.
Let's say that moving the label was an accident and that you want the view's position to match the existing constraints.
我们假设label 的拖动是个意外,你需要view的position来匹配已经有的限制。
Select the date label. Then, in the Auto Layout constraints menu, select the icon to reveal the Resolve Auto Layout Issues menu
选择来解决auto layout issues.
Select Update Frames at the top. This will reposition the label to match its constraints.
选择update frame ,将会把重新放置label来匹配它的constraints.
On the other hand, if you wanted the constraints to change to match the new position of the view, you
would choose to Update Constraints.
另一方面如果你想让constraints 来匹配已有的位置,你可以选择
(1)Update Frames:Adjusts the frame of the view to match its constraints.
(2)Update Constraints:Adjusts the constraints of the view to match its frame.
(3)Add Missing Constraints
:For views with an ambiguous layout, this will add the necessary constraints to remove the ambiguity. However, the new constraints might not be what you want, so make sure to double-check and test this resolution.
对于一个ambiguous layout ,它能够添加必要的constraints。然而新的constraints 可能不是你想要得。所以一定要再检查一下。
(4)Reset to Suggested Constraints 重置被建议的constraints:This will remove any existing constraints from the view and add new constraints. These suggested constraints are sensitive to the context of the view. For example, if the view is near the top of its superview,the suggested constraints will probably pin it to the top, whereas if the view is near the bottom of its superview, it will probably be pinned to the bottom.
(5)Clear Constraints: All constraints are removed. If no explicit constraints are added to this view, it will have the default fixed position and size constraints added to it.
所有的constrains 将被移除。如果没有明确地限制添加到这个view上,它将会有默认的position 和size constraints 添加上去。