iOS Auto Layout Demystified(2nd)

About Those Missing Views

It’s common for developers new to Auto Layout to “lose” views. They discover that views they have added end up offscreen or that they have a zero size due to constraints. (Incidentally, Auto Layout works with positive sizes, zero or larger. You cannot create views with negative widths or heights.) The missing views problem catches many devs. This problem happens with both underconstrained views and views with inconsistent rules.一些“丢失的”views它们添在屏幕外面或者约束的大小为0,Auto Layout是正数的大小0或者更大。你不能创建一个负的的宽度或高度的views。

1.Underconstrained Missing Views

Underconstrained views don’t give Auto Layout enough information to build from, so it often defaults to a size of zero.约束过少的views没给Auto Layout足够信息所以它经常默认的大小为0. Consider the following example. This code creates a new view, prepares it for Auto Layout, and then adds two sets of constraints, which I’ve highlighted in boldface:

// Create a new view and add it into the Auto Layout system 
// This view goes missing despite the initWithFrame: size 
UIView *view = [[UIView alloc]
initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; 
[self.view addSubview:view]; view.translatesAutoresizingMaskIntoConstraints = NO;

// Add two sets of rules, pinning the view and setting height
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|[view(==80)]" // 80 height 
options:0 
metrics:nil 
views:NSDictionaryOfVariableBindings(view)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
2013-01-14 10:47:40.460 HelloWorld[73891:c07] 
<UIView: 0x884dfc0; frame = (0 0; 0 80); layer = <CALayer: 0x884e020>>

2.Missing Views with Inconsistent Rules

Inconsistent rules may also produce views that are missing in action. For example, imagine a pair of rules that say “View A is three times the width of View B” and “View B is twice the width of View A.” The following code snippets implement these rules. I’ve boldfaced the parts of the code that tell the rule story:下面的约束一个是View A的宽度是View B的3倍,另一个是View B的宽度是View A的2倍

NSLayoutConstraint *constraint; 
constraint = [NSLayoutConstraintconstraintWithItem:viewA 
attribute:NSLayoutAttributeWidth 
relatedBy:NSLayoutRelationEqual 
toItem:viewB 
attribute:NSLayoutAttributeWidth 
multiplier:3.0f 
constant:0.0f];
[self.view addConstraint:constraint];

constraint = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeWidth 
relatedBy:NSLayoutRelationEqual 
toItem:viewB 
attribute:NSLayoutAttributeWidth 
multiplier:2.0f 
constant:0.0f];
[self.view addConstraint:constraint];

Surprisingly, these two rules are neither unsatisfiable nor ambiguous, even though common sense suggests otherwise. That’s because both rules are satisfied when View A and View B have zero width. At zero, View A’s width can be three times the width of View B, and View B twice the width of View A:只有当View A 和View B的宽度都为0的时候才能满足上面的约束。

 0 = 0 * 3and0 = 0 * 2

When this code is run and the rules are applied, the views present the zero-width frames expected from this scenario:

2013-01-14 11:02:38.005 HelloWorld[74460:c07]
<TestView: 0x8b30910; frame = (320 454; 0 50); layer = <CALayer: 0x8b309d0>>
2013-01-14 11:02:38.006 HelloWorld[74460:c07]
<TestView: 0x8b32570; frame = (320 436; 0 68); layer = <CALayer: 0x8b32450>>

3.Tracking Missing Views

You can track down “missing” views with the debugger by inspecting their geometry after you expect them to appear 在debugger时你可以追踪“missing” views通过检查它们的几何形状(for example, in viewDidAppear: and awakeFromNib). You may want to add NSAssert statements about their expected size and positions. Some will be, as discussed, zero sized.

Intrinsic Content Size

With Auto Layout, a view’s content plays as important a role in its layout as its constraints. This is expressed through each view’s intrinsicContentSize, which describes the minimum space needed to express the full view content without squeezing or clipping that data. It derives from the natural properties of the content that each view presents.

For a button, the intrinsic content size with its title. As a title grows or shrinks, the button’s intrinsic content size adjusts to match. This snippet creates a button and assigns it a pair of titles, and it reports the intrinsic content size after each assignment:对于一个button,它的本质的大小就是它的title。

UIButton *button =[UIButton buttonWithType:UIButtonTypeSystem];

[button setTitle:@"Hello World" forState:UIControlStateNormal];
NSLog(@"%@: %@", [button titleForState:UIControlStateNormal],NSStringFromCGSize(button.intrinsicContentSize));

[button setTitle:@"On" forState:UIControlStateNormal];
NSLog(@"%@: %@", [button titleForState:UIControlStateNormal],NSStringFromCGSize(button.intrinsicContentSize));

When run, this snippet outputs the following sizes:

2013-07-02 12:16:46.576 HelloWorld[69749:a0b] Hello World: {78, 30}
2013-07-02 12:16:46.577 HelloWorld[69749:a0b] On: {30, 30}

When you change a view’s intrinsic contents, you need to call invalidateIntrinsicContent Size to let Auto Layout know to at its next layout pass.当你改变一个view的intrinsic contents,你需要调用invalidateIntrinsicContent让Auto Layout知道重新计算layout pass