Allen Chiang

iOS程序猿的blog

© 2014. All rights reserved.

Auto Layout - iOS.6.by.tutorials笔记

因为接下来的其实是原作者Matthijs Hollemans对于Auto Layout的进阶篇内容,所以建议先阅读一下作者另外两篇关于Auto Layout基础的 在著名的raywenderlish上面有,链接如下:

Beginning Auto Layout in iOS 6: Part 1/2

Beginning Auto Layout in iOS 6: Part 2/2

魔法程式

A = B*m + c

view1.attribute = view2.attribute * multiplier + constant

同时中间的等于号你也可以使用>=或者<=来代替

通过代码添加约束

通过NSLayoutConstraint的类方法来创建一个约束:

+ (instancetype)constraintWithItem:(id)view1 
 attribute:(NSLayoutAttribute)attr1 
 relatedBy:(NSLayoutRelation)relation 
    toItem:(id)view2 
 attribute:(NSLayoutAttribute)attr2 
multiplier:(CGFloat)multiplier 
  constant:(CGFloat)c

上面constraint语法格式的含义,参考上面提到的魔法程式

image

简单来看就是

button1.width = 200.0f

NSLayoutConstraint有一个属性名称:priority

@property UILayoutPriority priority

enum {
	UILayoutPriorityRequired = 1000,
	UILayoutPriorityDefaultHigh = 750,
	UILayoutPriorityDefaultLow = 250,
	UILayoutPriorityFittingSizeLevel = 50,
};
typedef float UILayoutPriority;	

可以看到这其实是一个float的数字,在iOS上应该介于0~1000之间。数字越大的约束将优先于数字小的生效。

部分系统控件默认带有保持自身固有尺寸的约束,比如button(个人感觉这里跟Android的wrapcontent有点像)。这个默认约束的优先级是250,也就是priority=250。所以如果你添加的控件自身尺寸大小的约束要大于250才能生效;另外系统也提供了一个方法来降低这个默认约束的优先级:

setContentHuggingPriority:forAxis:

添加约束的规则

  1. 如果是一个view和他的superview的约束,添加到他的superview;
  2. 是同一个superview下的两个view的约束,添加到这个superview;
  3. 只是针对一个view的约束,添加给他自己。比如width=height之类的

VFL = Visual Format Language

NSLayoutConstraint同样支持使用vfl来创建一个约束,目的是比上面的更简洁

+ (NSArray *)constraintsWithVisualFormat:(NSString*)format 
options:(NSLayoutFormatOptions)opts 
metrics:(NSDictionary *)metrics 
  views:(NSDictionary *)views

一些相对经典的vfl语句

|-[deleteButton]-(>=8)-[cancelButton]-[nextButton]-|

|-5-[deleteButton]-(>=8)-[cancelButton]-30-[nextButton]-|

|-[deleteButton]-(>=8)-[cancelButton(120)]-[nextButton(30)]-|

|-[deleteButton(==nextButton@700)]-(>=8)- [cancelButton(==nextButton@700)]-[nextButton]-|

#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)

V:[nextButton]-distance-|

其中的distance需要通过metrics dictionary传入

constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[nextButton]-distance-|" 	options:0 
metrics:@{ @"distance": @50 }
  views:viewsDictionary];`

动态布局

前面都是静态的自动布局,一旦确定了约束就确定了控件的位置,无法改变。

但是实际情况中,我们的控件往往是需要响应用户的事件,并展现出如位置、大小等的变化。以前我们大都通过setFrame等方式来实现,那伴随约束的情况下又如何实现呢?

为了便于对约束的管理和维护,我们应该把所有的约束统一的写在重载了UIViewController的updateViewConstraints方法,或者UIView的updateConstraints方法中。

- (void)updateConstraints
- (void)updateViewConstraints

每当布局展现之前,系统会自动调用上述方法。

对于UIView你也可以通过手动调用- setNeedsUpdateConstraints方法,来告诉系统布局约束需要被更新,系统会在将来自动调用上面- updateConstraints方法。

但是对于UIViewController好像就没有手动触发的方法了,可能要直接调用- updateViewConstraints方法了。

动画约束

之前做动画可能都是通过设置frame的绝对位置来实现,但是在使用了Auto Layout以后该怎么实现动画效果呢?

有如下两种方法:

1.去除旧的约束,添加新的约束

我们通过删除旧的约束,添加新的约束来实现变化,至于普通的动画么,我们也可以使用简单的方法:

[UIView animateWithDuration:0.3f animations:^{
	[self.view layoutIfNeeded];
}

2.改变已有动画的常量值

还记得上面的方程式么?A = B*m + c

我们可以通过改变c值的大小来实现动画的效果

区别在于:xxxIfNeeded方法是立即执行,而setNeedsxxx则是延迟到UIKit的下一个空闲时间片,参考RunLoop的概念。

自定义view的固有尺寸

通过重写UIView的-(CGSize)intrinsicContentSize方法返回内部尺寸大小。则Auto Layout会使用这个尺寸大小来计算布局。

comments powered by Disqus