iOS Programming Auto Layout: Programmatic Constraints
1.
However, if your views are created in code, then you will need to constrain them programmatically.
如果你的view是由代码创建的,那么你需要用编程限制它了。
To have a view to work with, you are going to recreate the image view programmatically and then constrain it in the UIViewController method viewDidLoad. This method will be called after the NIB file for BNRDetailViewController's interface has been loaded.
viewDidLoad方法将会被调用在NIB文件被加载后。
If you are creating and constraining an entire view hierarchy, then you override loadView.
如果你要创建和constraining 整个view hierarchy ,你需要重写loadView。
If you are creating and constraining an additional view to add to a view hierarchy that was created by loading a NIB file, then you override viewDidLoad instead.
如果你creating 和constraining 一个另外的view并加入一个由NIB文件加载的view hierarchy,你需要重写viewDidLoad.
In BNRDetailViewController.m, implement viewDidLoad to create an instance of UIImageView.
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *iv = [[UIImageView alloc] initWithImage:nil];
// The contentMode of the image view in the XIB was Aspect Fit: iv.contentMode = UIViewContentModeScaleAspectFit;
// Do not produce a translated constraint for this view iv.translatesAutoresizingMaskIntoConstraints = NO;
// The image view was a subview of the view [self.view addSubview:iv];
// The image view was pointed to by the imageView property
self.imageView = iv; }
The line of code regarding translating constraints has to do with an older system for scaling interfaces – autoresizing masks.
考虑到translating constraints 与scaling interface -autoresizing masks 有关。
Every view has an autoresizing mask. By default, iOS creates constraints that match the autoresizing mask and adds them to the view.
每个view都有一个autoresizing mask. 默认情况下,iOS 创建constraints 匹配autoresizing mask 并把他们添加到view上。
These translated constraints will often conflict with explicit constraints in the layout and cause an unsatisfiable constraints problem. The fix is to turn off this default translation by setting the property translatesAutoresizingMaskIntoConstraints to NO.
这些translated constraints 会语我们明确指出的constraints 并导致unsatisfiable constraints problem.修复的办法是通过设置translatesAutoresizingMaskIntoConstraints为NO来关闭default translation.
The UIImageView should span the entire width of the screen and should maintain the standard 8 point spacing between itself and the dateLabel above and the toolbar below. Here are the constraints for the image view spelled out:
(1)left edge is 0 points from the image view's container
(2)right edge is 0 points from the image view's container
(3) top edge is 8 points from the date label
(4)bottom edge is 8 points from the toolbar
Apple recommends using a special syntax called Visual Format Language (VFL) to create constraints programmatically.
苹果推荐使用一个专门的syntax 叫做Visual format language(VFL)来创建constraints programmatically.
This is how you will constrain the image view. However, there are times when a constraint cannot be described using VFL.n those cases, you must take another approach.
2. Visual formate language
Visual Format Language is a way of describing constraints in a literal string.
它是一种literal string 来描述constraints.
You can describe multiple constraints in one visual format string.
A single visual format string, however, cannot describe both vertical and horizontal constraints.
一个visual format string不能表述既有vertical又有horizontal的constraints.
Thus, for the image view, you are going to come up with two visual format strings: one that constrains the horizontal spacing of the image view and one that constrains its vertical spacing.
Here is how you would describe the horizontal spacing constraints for the image view as a visual format string:
这个就是你怎样用visual format string 表述horizontal spacing constrictions :
@"H:|-0-[imageView]-0-|"
The H: specifies that these constraints refer to horizontal spacing. The view is identified inside square brackets.
H 表明这个限制指向的是水平。这个view的identifier被方括号包围。
The pipe character (|) stands for the view's container.
| 代表了view的container。
This image view, then, will be 0 points away from its container on its left and right edges.
这个image view 将会是离它的container 的左侧和右侧边0points。
When the number of points between the view and its container (or some other view) is 0, the dashes and the 0 can be left out of the string:
@"H:|[imageView]|"
如果是0的话还可以简写成以上。
The string for the vertical constraints looks like this:
vertical constraints 的string 像这样:
@"V:[dateLabel]-8-[imageView]-8-[toolbar]"
Notice that "top" and "bottom" are mapped to "left" and "right", respectively, in this necessarily horizontal display of vertical spacing. The image view is 8 points from the date label at its top edge and 8 points from the toolbar at its bottom edge.
You could write this same string like this: 你也可以写成这样:
@"V:[dateLabel]-[imageView]-[toolbar]"
The dash by itself sets the spacing to the standard number of points between views, which is 8.
dash 被它自己设置spacing 为标准的number of points 在两个view 间,就是8.
Imagine you had two image views with the following horizontal constraints:
考虑你有两个view在水平的constraints:
(1)the horizontal spacing between the image views should be 10 points
两个image view 的水平spacing应该是10 points。
(2)the lefthand image view's left edge should be 20 points from its superview
(3)the righthand image view's right edge should be 20 points from its superview
You could describe the three constraints in one visual format string:
@"H:|-20-[imageViewLeft]-10-[imageViewRight]-20-|"
The syntax for a fixed size constraint is simply adding an equality operator and a value in parentheses inside a view's visual format:
这个syntax为一个fixed size constraint 是简单地添加一个equality operator 和一个在括号里地值在一个view 的visual format:
@"V:[someView(==50)]"
This view's height would be constrained to 50 points.
3. Creating Constraints 创建 constraints
A constraint is an instance of the class NSLayoutConstraint. When creating constraints programmatically, you explicitly create one or more instances of NSLayoutConstraint and then add them to the appropriate view object.
constraint 是NSLayoutConstraint的一个实例。当你创建一个 constraints programmatically,你明确地创建了一个或多个NSLayoutConstraint实例,并把它们添加到对应的view object上。
Creating and adding constraints is one step when working with a XIB, but it is always two distinct steps in code.
creating and add constraints 是一步当用XIB时,但是在code里就是两步:
You create constraints from a visual format string using the NSLayoutConstraint method:
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary *)metrics views:(NSDictionary *)views
This method returns an array of NSLayoutConstraint objects because a visual format string typically creates more than one constraint.
这个方法返回一列NSLayoutConstraint对象因为 visual format string一般都是创建多余一个的constraint.
The first argument is the visual format string. For now, you can ignore the next two arguments, but the fourth is critical.
The fourth argument is an NSDictionary that maps the names in the visual format string to view objects in the view hierarchy.
第四个参数是在visual format string 的名字到在view hierarchy 中的view 对象的名字的一个map。
The two visual format strings that you will use to constrain the image view refer to view objects by the names of the variables that point to them.
@"H:|-[imageView]-|" @"V:[dateLabel]-[imageView]-[toolbar]"
However, a visual format string is just a string, so putting the name of a variable inside it means nothing unless you explicitly make the association.
把变量的名字放进format string在你指明了他们的关系之前是毫无意义的。
In BNRDetailViewController.m, create a dictionary of names for the views at the end of viewDidLoad.
NSDictionary *nameDictionary=@{@"imageView": self.imageView,@"dateLabel":self.dateLabel,@"toolBar":self.toolbar};
You are using the names of your variables as keys, but you can use any key to name a view. The only exception is the | character, which is a reserved name for the superview (container) of the views being referenced in the string.
你可以用任意的关键字命名一个view。
Next, in BNRDetailViewController.m, create the horizontal and vertical constraints for the image view:
// imageView is 0 pts from superview at left and right edges
NSArray *horizontalConstraints=[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[imageView]-0-|" options:0 metrics:0 views:nameDictionary];
// imageView is 8 pts from dateLabel at its top edge... // ... and 8 pts from toolbar at its bottom edge NSArray *verticalConstraints =
NSArray *verticalConstraints=[NSLayoutConstraint constraintsWithVisualFormat:@"V:[dateLabel]-[imageView]-[toolBar]" options:0 metrics:nil views:nameDictionary];
4.Adding constraints
You now have two arrays of NSLayoutConstraint objects. However, these constraints will have no effect on the layout until you explicitly add them using the UIView method
你已经有两组NSLayoutConstraints objects.但是如果你不明确地用UIView 方法把他们加进去,这些constraints是没有用得。
- (void)addConstraints:(NSArray *)constraints
Which view should receive the addConstraints: message?
什么时候view应该接受addConstraints消息?
Usually, the closest common ancestor of the views that are affected by the constraint.
一般是受constraint影响离最近的view公共的祖先。
Here is a list of rules you can follow to determine which view you should add constraints to:
下面是一些你往哪个view添加这些constraints的一些规则:
(1)If a constraint affects two views that have the same superview (such as the constraint labeled "A" in Figure 16.1), then the constraint should be added to their superview.
如果constraint影响有公共superview的两个view,那么constraint应该添加到它们的superview上。
(2)If a constraint affects just one view (the constraint labeled "B"), then the constraint should be added to the view being affected.
如果一个constraint仅仅影响一个view,那么这个constraint应该加在被影响的view上。
(3)If a constraint affects two views that do not have the same superview but do share a common ancestor much higher up on the view hierarchy (the constraint labeled "C"), then the first common ancestor gets the constraint.
如果一个限制影响的两个view,并没有共同的super view,而他们有共同的祖先,在view hierarchy中,那么应该加在他们的一个共同的祖先上。
(4)If a constraint affects a view and its superview (the constraint labeled "D"), then this constraint will be added to the superview.
如果一个祖先即影响了view及view 的superview,那么应该加在sup view上。
For the image view's horizontal constraints, this determination is easy. These constraints affect only the imageView and its superview, so you add them to the superview – the view of the BNRDetailViewController.
For the vertical constraints, the imageView, dateLabel, and toolbar are the affected views. They all share the same superview (the view of the BNRDetailViewController), so you also add these constraints to the superview.
In BNRDetailViewController.m, add both sets of constraints to the BNRDetailViewController's view at the end of viewDidLoad.
[self.view addConstraints:horizontalConstraints];
[self.view addConstraints:verticalConstraints];
5 Intrinsic Content Size
content size的本质
Intrinsic content size is information that a view has about how big it should be based on what it displays.
Intrinsic content size是一个view根据它要显示的大约应该多大的信息。
For example, a label's intrinsic content size is based on how much text it is displaying. In your case, the image view's intrinsic content size is the size of the image that you selected.
例如:label's intrinsic content size 根据它有多少文字要显示,而image view 的intrinsic content size 是你要选择的image 的size。
Auto Layout takes this information into consideration by creating intrinsic content size constraints for each view.
Auto Layout通过考虑这些信息通过为每个view创建 intrinsic content size constraints
Unlike other constraints, these constraints have two priorities: a content hugging priority and a content compression resistance priority.
不同于其他的限制,这些限制有两个priorities:content hugging priority和content compression resistence priority.
(1)Content hugging priority:tells Auto Layout how important it is that the view's size stay close to, or "hug", its intrinsic content. A value of 1000 means that the view should never be allowed to grow larger than its intrinsic content size. If the value is less than 1000, then Auto Layout may increase the view's size when necessary.
告诉auto layout 这个view's size 靠近或者hug 它的instrinsic content 是有多重要。1000代表这个view应该不允许比它的intrinsic content size 大。如果这个值小于1000,那么auto layout 应该根据当需要的时候增加view 的尺寸。
(2)content compression resistance priority :tells Auto Layout how important it is that the view avoid shrinking, or "resist compressing", its intrinsic content. A value of 1000 means that the view should never
be allowed to be smaller than its intrinsic content size. If the value is less than 1000, then Auto Layout may shrink the view when necessary.
告诉auto layout view 避免shrinking 或者抵制 compression ,它的intrinsic content 是有多重要。1000 意味着这个view永远不允许比它的intrinsic content size.如果小于1000,auto layout 应该允许缩小view 当需要的时候。
In addition, both priorities have separate horizontal and vertical values so that you can set different priorities for a view's height and width.
另外,两个priorities 都分别有 horizontal and vertical values所以你能为每个view的height and width 设置不同的优先级。
This makes a total of four intrinsic content size priority values per view.
这样每个view 都有四个intrinsic content size priority.
You can see and edit these values in Interface Builder. Reopen BNRDetailViewController.xib. Shift-click to select all three text fields in the canvas. Head to the inspector and select the tab to reveal the size inspector. Find the Content Hugging Priority and Content Compression Resistance
Priority sections.
First, notice that these values are not 1000 and thus will never conflict with the constraints that you have added so far.This is why the layout will appear incorrectly with smaller-sized images.
注意到这些值都不是1000,因此与现在添加的constraints 并不冲突。这也是你的layout 当有smaller-sized 的图像时,并没有正确地原因
The value text field's content hugging vertical property is 250, which is lower than that of the image view (which is 251), so when faced with a small image, Auto Layout chooses to make the text field taller than its intrinsic content size.
It would be better if the image view had a smaller vertical content hugging and commpression resistance priority than the other subviews.
如果image view 有一个更小得vertical content hugging 和compression resistance priority 比其他的subview。
Open BNRDetailViewController.m and update viewDidLoad to lower these priorities.
// Set the vertical priorities to be less than
// those of the other subviews
[self.imageView setContentHuggingPriority:200 forAxis:UILayoutConstraintAxisVertical];
[self.imageView setContentCompressionResistancePriority:700 forAxis:UILayoutConstraintAxisVertical];
6. The Other Way
There are times when a constraint cannot be created with a visual format string.
有些情况下,一个constraint 可能不能通过visual format string 创建。
For instance, you cannot use VFL to create a constraint based on a ratio, like if you wanted the date label to be twice as tall as the name label or if you wanted the image view to always be 1.5 times as wide as it is tall.
you can create an instance of NSLayoutConstraint using the method
这样你可以通过一个NSLayoutConstraint 的实例来创建通过这个方法:
+ (id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation toItem:(id)view2
attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier
constant:(CGFloat)c
This method creates a single constraint using two layout attributes of two view objects.
这个方法用两个view的两个layout attributes来创建一个constraint .
The multiplier is the key to creating a constraint based on a ratio.
multiplier是基于比例创建一个constriant 的关键。
The constant is a fixed number of points, like you have used in your spacing constraints.
这个常量是一个固定的point就像你用来spacing constraints一样。
The layout attributes are defined as constants in the NSLayoutConstraint class:
这个layout attributes 定义为NSLayoutConstraint类的一个常量。
NSLayoutAttributeLeft
NSLayoutAttributeRight
NSLayoutAttributeTop
NSLayoutAttributeBottom
NSLayoutAttributeWidth
NSLayoutAttributeHeight
NSLayoutAttributeBaseline
NSLayoutAttributeCenterX
NSLayoutAttributeCenterY
NSLayoutAttributeLeading
NSLayoutAttributeTrailing
Let's consider a hypothetical constraint.Say you wanted the image view to be 1.5 times as wide as it is tall.
你想让你的image view 的宽度是高度的1.5 倍。
You cannot do this with a visual format string, so you would create it individually instead with the following code.
NSLayoutConstraint *aspectConstraint = [NSLayoutConstraint constraintWithItem:self.imageView
attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
toItem:self.imageView attribute:NSLayoutAttributeHeight
multiplier:1.5 constant:0.0];
To understand how this method works, think of this constraint as the equation shown in
为了理解这个,我们可以把他们写成等式:
You relate a layout attribute of one view to the layout attribute of another view using a multiplier and a constant to define a single constraint.
To add a single constraint to a view, you use the method
- (void)addConstraint:(NSLayoutConstraint *)constraint
The same logic applies to decide which view should receive this message. When using this method, the determination is even easier to make because the affected view objects are the first and fourth arguments. In this case, the only affected object is the image view,
谁应该接受消息和上面的逻辑一样。 当你使用这个方法的时候,你能更容易的知道应该添加到哪个view上。因为第一和第四个参数已经指明了。
[self.imageView addConstraint:aspectConstraint];
7 NSAutoresizingMaskLayoutConstraint
Before Auto Layout, iOS applications used another system for managing layout: autoresizing masks.
在auto layout 之前,iOS应用使用另一套系统来管理layout:autoresizing masks。
Each view had an autoresizing mask that constrained the relationship between a view and its superview, but this mask could not affect relationships between sibling views.
每个view都有一个 autoresizing mask来限制一个view和它的superview 之间的关系,但是这个mask 不能影响两个sibling view 的关系。
By default, views create and add constraints based on their autoresizing mask. However, these translated constraints often conflict with your explicit constraints in your layout, which results an unsatisfiable constraints problem.
默认情况下,view 会create 和add 限制到他们的autoresizing mask。然而这些translated constraints经常与现在的layout冲突。
Let's look at the format of one of these constraints more closely.
让我们看看这些constraints限制的一个的格式:
<NSLayoutConstraint:0x9153fa0 UILabel:0x9149970.leading == UILabel:0x9149f00.leading>
This description indicates that the constraint located at memory address 0x9153fa0 is setting the leading edge of the UILabel (at 0x9149970) equal to the leading edge of the UILabel (at 0x9149f00).
The fifth, however, is an instance of NSAutoresizingMaskLayoutConstraint. This constraint is the product of the translation of the image view's autoresizing mask.
第五个NSAutoresizingMaskLayoutConstraint是这个image view 的autoresizing mask的一个产品。
Unfortunately, it chooses poorly and ignores one of your explicit instances of NSLayoutConstraint instead of the NSAutoresizingMaskLayoutConstraint.
The note before the constraints are listed is very helpful: the NSAutoresizingMaskLayoutConstraint needs to be removed.
// Do not produce a translated constraint for this view iv.translatesAutoresizingMaskIntoConstraints = NO;