布局的两种模式
1,基于父视图参考系的偏移frame 基于父视图比例自动补偿调整。
2,基于容器约束和内容压力求解视图位置 动态求解替代补偿模式
内容压力:负压力(吸引边框)正压力(排斥边框)
*通过alloc init出来的对象默认支持frame方法
*通过storyboard拖出来的默认支持auto layauto(约束)
平衡约束和内容压力的优先级
约束自身的宽和高,,约定宽高比
容器、父视图-----距离父视图的上下左右各是多少,距离中心
同级视图 宽高的关系,对齐
代码创建约束 创建约束宽度
view!.attr1<relation>multi;tier * view2.attr2 + c.
NSLayoutConstraint * c1 = [NSLayoutConstraint constraintWithItem: attribute:(NSLayoutAttribute) relatedBy: toItem: attribute: multiplier: constant: ];
NSLayoutConstraint * c1 = [NSLayoutConstraint constraintWithItem: attribute:(NSLayoutAttribute) relatedBy: toItem: attribute: multiplier: constant: ];
1.strut模式
strut的中文直译有支持的意思,其布局也取自此意。用通俗的翻译来讲这个机制就是:对父视图的固定偏移。在学习UIView对象之后,我们知 道,UIView对象可以有自身的子视图,同时负责维护子视图的位置。而UIView自身的位置,则是通过CGRect类型的结构体变量frame所描 述,描述内容之中便包含基于父视图的固定偏移量。
确定位置,首先需要确定参考系(也称坐标系)。只有在确定参考系之后,才能根据参考系确定自身的位置。在确定参考系时有两种情况:
UIWindow对象的参考系
非UIWindow的视图对象的参考系
首先我们来研究UIWindow对象的参考系,因UIWindow对象显示不需要父视图,所以规定其位置的参考系便是屏幕本身。
参考系的规定如下
左上角顶点为参考系原点
水平向右为x正方向
垂直向下为y正方向
参考系大小如下图所示,不同机型的参考系不一样
参考系大小于屏幕像素点关系
由于历史和当前市场产品的一些原因,现在所描述的视图参考系的大小并不等于手机屏幕真实的像素数量。以iPhone4为例,其屏幕像素点的个数为 高:960,宽:640。但描述屏幕的参考系大小为480×320。通过简单运算可知,屏幕像素为参考系的2倍,即一个参考系中的点,需要用4个像素去描 绘,所有iPhone的显示效果非常细腻。采用2倍大小的还有iPhone4s、iPhone5、iPhone5s、iPhone6
iPhone6 Plus的参考系和屏幕像素的关系较为特殊。6Plus的参考系大小如图所示为x:414,y:736。同时6Plus的显示芯片采用9个像素描绘一个点,使显示内容更加细腻。这样其显示芯片需要生成一个2208×1242的图像,为参考系大小的3倍。
但6Plus为兼容市面上绝大多数的大屏手机分辨率,自己的屏幕分辨率也设计为1920×1080。为了将显示芯片渲染的2208×1242图像显 示在1920×1080的屏幕上,在显示中,手机自动将原始图像缩小1.15倍(2208÷1.15=1920)。所以,面向6Plus进行开发工作时, 开发人员和设计人员眼中的6Plus分辨率应该为2208×1242,但用户看到的实际大小应该为1920×1080。
strut模式中,通过CGRect结构体变量描述一个基于参考系的矩形区域。其中包含该矩形区域左上角顶点在参考系中的位置和矩形自身的长和高。创建UIWindow对象时,通过UIScreen对象获取当前屏幕的高度和宽带用来创建UIWindow对象。
strut的中文直译有支持的意思,其布局也取自此意。用通俗的翻译来讲这个机制就是:对父视图的固定偏移。在学习UIView对象之后,我们知 道,UIView对象可以有自身的子视图,同时负责维护子视图的位置。而UIView自身的位置,则是通过CGRect类型的结构体变量frame所描 述,描述内容之中便包含基于父视图的固定偏移量。
确定位置,首先需要确定参考系(也称坐标系)。只有在确定参考系之后,才能根据参考系确定自身的位置。在确定参考系时有两种情况:
UIWindow对象的参考系
非UIWindow的视图对象的参考系
首先我们来研究UIWindow对象的参考系,因UIWindow对象显示不需要父视图,所以规定其位置的参考系便是屏幕本身。
参考系的规定如下
左上角顶点为参考系原点
水平向右为x正方向
垂直向下为y正方向
参考系大小如下图所示,不同机型的参考系不一样
参考系大小于屏幕像素点关系
由于历史和当前市场产品的一些原因,现在所描述的视图参考系的大小并不等于手机屏幕真实的像素数量。以iPhone4为例,其屏幕像素点的个数为 高:960,宽:640。但描述屏幕的参考系大小为480×320。通过简单运算可知,屏幕像素为参考系的2倍,即一个参考系中的点,需要用4个像素去描 绘,所有iPhone的显示效果非常细腻。采用2倍大小的还有iPhone4s、iPhone5、iPhone5s、iPhone6
iPhone6 Plus的参考系和屏幕像素的关系较为特殊。6Plus的参考系大小如图所示为x:414,y:736。同时6Plus的显示芯片采用9个像素描绘一个点,使显示内容更加细腻。这样其显示芯片需要生成一个2208×1242的图像,为参考系大小的3倍。
但6Plus为兼容市面上绝大多数的大屏手机分辨率,自己的屏幕分辨率也设计为1920×1080。为了将显示芯片渲染的2208×1242图像显 示在1920×1080的屏幕上,在显示中,手机自动将原始图像缩小1.15倍(2208÷1.15=1920)。所以,面向6Plus进行开发工作时, 开发人员和设计人员眼中的6Plus分辨率应该为2208×1242,但用户看到的实际大小应该为1920×1080。
strut模式中,通过CGRect结构体变量描述一个基于参考系的矩形区域。其中包含该矩形区域左上角顶点在参考系中的位置和矩形自身的长和高。创建UIWindow对象时,通过UIScreen对象获取当前屏幕的高度和宽带用来创建UIWindow对象。
UIScreen * screen = [UIScreen mainScreen];
CGSize size = screen.bounds.size;
CGPoint orgion = CGPointMake(0, 0);
CGRect frame;
frame.origin = orgion;
frame.size = size;
UIWindow * window = [[UIWindow alloc]initWithFrame:frame];
[window makeKeyAndVisible];
确定UIWindow位置和大小的参考系就是屏幕参考系。也可以说,window对象的位置是基于屏幕。
接下来我们继续研究非UIWindow的视图对象的参考系。因非UIWindow的视图对象需要加入视图树中才能够被显示,也就是需要作为已经显示在屏幕上的视图的子视图。
能够在屏幕上看到的视图对象一定有一个父视图,这个父视图对象便是子视图的参考系。默认情况下,参考系的原点为父视图的左上角顶点,参考系的边界为父视图的边界。但此默认情况可以通过配置bounds属性调整。
UIView * redView = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
表示创建了一个视图,这个视图的左上角顶点,距离父视图左上角顶点偏移了(50,50),自身的大小为100×100。此时无法确定该视图在屏幕上的位置,因还不知道父视图在屏幕上的位置。所有这里的frame属性仅仅是基于父视图的偏移量。
如果子视图自身大小超出了父视图的边界,也就是参考系边界,那么超出部分是否显示由父视图决定,通过父视图的clipsToBounds属性
// When YES, content and subviews are clipped to the bounds of the view.
//Default is NO.
@property(nonatomic) BOOL clipsToBounds;
bounds属性
上文中说,视图参考系默认情况下是父视图提供的,其原点为父视图的左上角顶点,参考系边界为父视图边界。但也可以通过配置bounds属性的值来调整参考系原点和边界大小。
// default bounds is zero origin, frame size.
@property(nonatomic) CGRect bounds;
在配置视图的frame属性时,bounds属性也会自动变换,通过注释可以看出,其origin值为zero,size值为frame中的size值。
bounds的origin主要是改变子视图参考系原点的位置。默认情况下origin为(0,0)表示:父视图左上角顶点是子视图参考系的原点。 但如果通过代码改变其值为(10,10),那么父视图左上角顶点将不再是子视图参考系的原点,而是(10,10)点,原点向上移动10个点,向左移动10 个点。这是,子视图的frame没有变换,但是父视图参考系发送变换,同样会引起子视图的位置改变。这个属性的使用场景这里暂不展开讲解。在之后章节中, 会进行深入介绍。
同样,我们可以通过bounds的size值来改变提供子视图参考系的边界大小。通常开发中不会主动调整此值,以免带来未知问题,例如按钮不能点击等,因为参考系边界不仅和现实相关还与时间传递有关,这里暂不深入。
2.spring模式
spring模式是位置的一种补偿模式。通过上文中的strut模式可以确定一个子视图在父视图中的固定偏移量。但是再实际使用时,常常会出现父视 图大小会发生改变的情况。比如App有竖屏模式转为横屏模式,此时如果不重新调整子视图相对于父视图的偏移量,将会出现现实错误。例如下图:
这是便需要根据父视图参考系的变化情况,提供一个动态的补偿机制,spring便是提供补偿机制的模式。
补偿机制是双休的,所以需要父视图的参考系发生变化时告知子视图,同时子视图需要根据变化做出相应的响应。
父视图通过autoresizesSubviews属性值来判断,是否告知子视图,本身的参考系发生变化。
// default is YES.
//if set, subviews are adjusted according to their autoresizingMask if self.bounds changes
@property(nonatomic) BOOL autoresizesSubviews
子视图通过autoresizingMask属性值做出相应的响应。
// simple resize. default is UIViewAutoresizingNone
@property(nonatomic) UIViewAutoresizing autoresizingMask;
UIViewAutoresizing为响应情况枚举:
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
autoresizingMask的默认值为UIViewAutoresizingNone,表示子视图的位置和大小按照当前在父视图参考系中的比例进行调整。调整方式如下图所示:
如果将autoresizingMask的值设置为枚举值中内容,那么该内容表示的尺寸将不做调整,例如UIViewAutoresizingFlexibleWidth表示,子视图本身的宽带将不会随父视图参考系变化二变化,如图所示
spring模式下的调整只能进行按比例缩放和固定,无法根据具体的要求进行补偿,现在基本不再使用此模式。
3.Auto Layout
AutoLayout模式是iOS6 SDK所支持的新机制。使用AutoLayout可以在不久层次结构中表示一个视图与自身、父视图、兄弟视图的关系。这些关系可以包含等式关系和不等式关 系,也可能涉及视图的属性,例如位置、范围。这些特性使AutoLayout能够以一种spring模型无法实现的方式实现精妙的边界条件。举例来说,你 可能会说“我想让这些等尺寸的视图排成一行,但是和父视图的边界距离不得小于20个点”或者“如果水平空间不足,我想在任何右边按钮受到影响前,让左边按 钮的文本先被裁减”。
代码中默认使用strut和spring模式。但从对spring和AutoLayout的功能描述上来看,两个功能明显冲突,都是对视图的布局进 行调整,spring是根据固定模式进行调整,而AutoLayout是需要根据约束进行求解。所有在同一个视图对象上,只能使用两种模式中的一共。需要 手动关闭strut和spring模式,才能使用AutoLayout模式。
关闭strut和spring的方法是通过视图对象的一个方法,通过设置flag为NO值,关闭strut和spring布局模式。
- (void)setTranslatesAutoresizingMaskIntoConstraints:(BOOL)flag
在AutoLayout模式下,每个视图对象在屏幕上的位置都是通过约束和内容压力求解出来的。
3.1约束
约束就是视图几何关系的限制条件,主要可以分成三类:
自身约束:宽度约束、高度约束
相对父视图约束:视图四边与父视图四边的距离、视图水平中心与父视图水平中心的距离、视图垂直中心与父视图垂直中心的距离。
兄弟视图间约束:水平间距,垂直间距,边缘对其,中心对其,尺寸匹配
兄弟视图定义:拥有相同父视图的视图对象。
创建约束的方法有三种:
使用生成约束的方法创建约束
使用约束描述字符串生成约束
使用Interface Builder添加约束
首先我们研究通过方法创建约束
UIView * view1;
UIView * view2;
NSLayoutAttribute att1;
NSLayoutAttribute att2;
CGFloat multiplier;
CGFloat constant;
NSLayoutRelation relation;
NSLayoutConstraint * constraint = [NSLayoutConstraint constraintWithItem:view1 attribute:att1 relatedBy:relation toItem:view2 attribute:att2 multiplier:multiplier constant:constant];
NSLayoutAttribute为属性枚举参数:
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
Left,Right,Top,Bottom为视图的四个边框
Leading,Trailing为视图的前边和后边。此处前边和后边是针对阅读顺序而言,对用中文,英文类的语言,左边就是前边,右边就是后边, 而对用阿拉伯文等从右向左阅读的文字,前边就是右边,后边就是左边。通常建议使用前后做约束,避免使用左右,这样可以更好的进行国际化的适配。
Width,Height表示视图自身的宽高属性
CenterX,CenterY依次表示视图自身水平中心和垂直方向的中心
Baseline为显示文字类组件特有的属性,表示文字显示的基线
NSLayoutRelation为关系枚举参数
NSLayoutRelationLessThanOrEqual = -1,
NSLayoutRelationEqual = 0,
NSLayoutRelationGreaterThanOrEqual = 1,
分别可以设置等于,小于等于,大于等于。这里在求解约束时,不等式较难理解。通常换一个角度去理解这个不等式,比如小于等于意思就是设置最大值,大于等于的意思就是设置最小值。
这段代码演示了创建约束的通用方式,其约束规定的内容为view1.attr1 关系 view2.attr2 * multiplier + constant。
下面我们依次举3个列子来讲解如何创建约束:
创建一个viewA宽度为10个点的约束
创建一个viewA的左边界距离父视图左边界20个点的约束
创建一个viewA的右边界距离viewB的右边界30个点的约束
UIView * fatherView = [[UIView alloc]initWithFrame:CGRectZero];
fatherView.translatesAutoresizingMaskIntoConstraints = NO;
UIView * viewA = [[UIView alloc]initWithFrame:CGRectZero];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewA];
UIView * viewB = [[UIView alloc]initWithFrame:CGRectZero];
viewB.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewB];
//创建一个viewA宽度为10个点的约束
NSLayoutConstraint * constraint_viewA_width = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:10];
//装载约束到父视图上
[fatherView addConstraint:constraint_viewA_width];
创建一个viewA的左边界距离父视图左边界20个点的约束
NSLayoutConstraint * constraint_superView_20_viewA = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:fatherView attribute:NSLayoutAttributeLeading multiplier:0 constant:20];
//装载约束到父视图上
[fatherView addConstraint:constraint_superView_20_viewA];
//创建一个viewA的右边界距离viewB的右边界30个点的约束
NSLayoutConstraint * constraint_viewA_30_viewB = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:viewB attribute:NSLayoutAttributeLeading multiplier:0 constant:30];
//装载约束到父视图上
[fatherView addConstraint:constraint_viewA_30_viewB];
>代码中用NSLayoutAttributeLeading表示左边,NSLayoutAttributeTrailing表示右边
>视图对象的约束一定要装载到其父视图身上,视图对象的自我约束力不够,需要父视图管理才可以。
接下来我们继续研究通过约束描述字符串生成约束用字符串主要描述一下两点
* 边界之间的间距
* 本身宽高
设计一下3个关键符号
* `| `表示父视图边界,根据字符串语义判定哪个边界
* `[] `表示视图对象,例如`[aView] `表示aView视图对象,通过一个`@{@"aView":aView} `字典进行绑定
* `-x- `表示距离,x为数组或字符,例如`-20- `或者`-tempX- `,通过一个`@{@"tempX":@20} `字典对字符串中的tempX进行数值绑定。
* `H: `表示水平方向
* `V: `表示垂直方向
举例:
* `H:[aView(100)] `表示aView的宽带为100
* `H:|-20-[aView] `表示aView的左边界距离父视图的左边界为20个点。
* `V:[aView]-10-| `表示aView的下边界距离父视图的下边界为10个点。
* `V:[aView(30)] `表示aView的高度为30
采用字符串描述约束的核心优势是可以同时描述多个视图对象
* `H:|-20-[aView]-40-[bView(==aView)]-20-| `表示,aView和bView等宽,其间距40,分别距离父视图两边20。
* `V:|-20-[aView(40)][bView(50)] `表示,aView高度40,距离父视图上边界20,bView高度50紧贴aView下边。
通过NSLayoutConstraint的constraintsWithVisualFormat方法将描述约束的字符串生成约束对象。
NSString * constraint_string = @"|-x-[viewA(20)]";
NSDictionary merticsDic = @{@"x":@10}; NSDictionary viewsDic = @{@"viewA":viewA};
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraint_string options:NSLayoutFormatDirectionLeadingToTrailing metrics:merticsDic views:viewsDic];
[fatherView addConstraints:constraints];
>方法中options参数是枚举值,主要描述文字的阅读方式是从左到右,还是从右到左,通常选择NSLayoutFormatDirectionLeadingToTrailing表示根据语言环境决定从前到后。
>
NSLayoutFormatDirectionLeadingToTrailing, NSLayoutFormatDirectionLeftToRight, NSLayoutFormatDirectionRightToLeft,
>方法中的metrics是一个字典,字典中的key为字符串中描述数值的字符,value为该字符对应的真实值。
>方法中的views是一个字典,字典中的key为字符串中描述视图对象的字符,value为该字符对应的真实对象。为了方便创建此类型字典UIKit中提供一个宏方便创建字典`NSDictionaryOfVariableBindings(<#...#>) `。
>
NSDictionary * dic = NSDictionaryOfVariableBindings(aView); 相当于@{@"aView":aView}字典
>因一个字符串中可以描述多个约束,所以返回一个数组,数组中为此字符串描述的所有约束对象。
>通过调用父视图的addConstraints方法,一次装载多个约束。
下面我们重复上节的3个列子来演示如何用描述字符串创建约束
* 创建一个viewA宽度为10个点的约束
* 创建一个viewA的左边界距离父视图左边界20个点的约束
* 创建一个viewA的右边界距离viewB的右边界30个点的约束
UIView * fatherView = [[UIView alloc]initWithFrame:CGRectZero];
fatherView.translatesAutoresizingMaskIntoConstraints = NO;
UIView * viewA = [[UIView alloc]initWithFrame:CGRectZero];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewA];
UIView * viewB = [[UIView alloc]initWithFrame:CGRectZero];
viewB.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewB];
//创建绑定字典
NSDictionary * views = NSDictionaryOfVariableBindings(fatherView,viewA,viewB);
//创建一个viewA宽度为10个点的约束描述字符串
NSString * constraintString1 = @"H:[viewA(10)]";
//通过字符串生成约束
NSArray * constraints1 = [NSLayoutConstraint constraintsWithVisualFormat:constraintString1 options:0 metrics:nil views:views];
//装载约束到父视图上
[fatherView addConstraints:constraints1];
//创建一个viewA的左边界距离父视图左边界20个点的约束
NSString * constraintString2 = @"H:|-20-[viewA]";
//通过字符串生成约束
NSArray * constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:constraintString2 options:0 metrics:nil views:views];
//装载约束到父视图上
[fatherView addConstraints:constraints2];
//创建一个viewA的右边界距离viewB的右边界30个点的约束
NSString * constraintString3 = @"H:[viewA]-30-|";
//通过字符串生成约束
NSArray * constraints3 = [NSLayoutConstraint constraintsWithVisualFormat:constraintString3 options:0 metrics:nil views:views];
//装载约束到父视图上
[fatherView addConstraints:constraints3];
>通过对比可以看出,通过描述字符串生成约束的方法更加简单直观。当然,更简单的方式是通过IB(Interface Builder)添加约束,关于IB的使用,请参加视频教程。
**3.2内容压力**
---
内容压力可以影响装载内容的对象外形尺寸。这里用UIImageView对象举例子,其他装载内容的视图对象用法一致。
在AutoLayout模式下,内容可以对组件边界产生两种压力
* 正压力:迫使组件改变自身的大小来显示完整的内容。
* 负压力:迫使组件维持当前的大小来显示完整内容。
这样描述比较抽象,举个例子:
现在有一张200×200的图片需要用一个UIImageView视图对象来显示。
**正压力**
如果我这是UIImageView视图对象的宽高都为100,那么内容会产生正压力,迫使视图的宽高都变为200,这样才能完整显示图片内容。
**负压力**
如果我这是UIImageView视图对象的宽高都为300,那么内容会产生负压力(吸引力),迫使视图的宽高都变为200,这样才能完整显示图片内容,而不留空白。
内容产生的压力能否是视图尺寸发送改变取决于以下两个因素:
* 压力的大小
* 约束力的大小
>这里的大小在代码中用优先级来描述,优先级高的代表力量大,在求解时起主导作用。每一个约束都可以配置其优先级,同时也可以配置内容压力的优先级。
压力的优先级通过两个方法分别进行配置
//设置正压力的大小 [viewA setContentCompressionResistancePriority:<#(UILayoutPriority)#> forAxis:<#(UILayoutConstraintAxis)#>]
//设置负压力的大小 [viewA setContentHuggingPriority:<#(UILayoutPriority)#> forAxis:<#(UILayoutConstraintAxis)#>]
>设置压力时,需要通过Axis方法参数制定水平还是数值方向,UILayoutConstraintAxis是枚举类型,其枚举值为UILayoutConstraintAxisHorizontal和UILayoutConstraintAxisVertical。
>压力优先级为CGFloat类型,范围是1-1000。其中系统给定了几个默认值:
>
UILayoutPriorityRequired=1000
UILayoutPriorityDefaultHigh=750
UILayoutPriorityDefaultLow=250
UILayoutPriorityFittingSizeLevel=50
约束力的优先级通过约束的`priority `配置,在约束装载到视图之前可以任意更改优先级,一旦,约束装载到了视图上,约束的优先级便不可再更改,如果需要更该,需要删除之前的约束,重新创建。
@property UILayoutPriority priority;
其类型与压力优先级一致。
下面我们通过代码演示一下内容压力和约束力直接的较量。还是通过本节一开始的例子,一个图片大小为200×200。一个UIImageView的宽高约束为100×100。此时,UIImageView对象的最终大小由内容压力和约束较量之后决定。
如果内容压力大于约束力,最终UIImageView的大小为200×200
//获取一个图片对象,该图片大小为200×200
UIImage * image = [UIImage imageNamed:@"testImage.jpg"];
//创建一个UIImageView显示该图片
UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
//设置其内容在水平和垂直方向的压力为501
[imageView setContentCompressionResistancePriority:501 forAxis:UILayoutConstraintAxisHorizontal];
[imageView setContentCompressionResistancePriority:501 forAxis:UILayoutConstraintAxisVertical];
//将视图对象贴在视图控制器的根视图上
[self.view addSubview:imageView];
//创建距离父视图左边间接约束
NSArray * c1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c1];
//创建距离父视图上边间接约束
NSArray * c2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c2];
//创建视图宽带和高度的约束,并设置其约束力为500小于501
NSLayoutConstraint * c3 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c3.priority = 500;
[self.view addConstraint:c3];
NSLayoutConstraint * c4 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c4.priority = 500;
[self.view addConstraint:c4];
运行效果如下图所示
![](/fileserve/userimg/201506/0e79989c1a1a11e583b097589ede9018.png)
如果内容压力小于约束力
self.view.backgroundColor = [UIColor whiteColor];
//获取一个图片对象,该图片大小为200*200
UIImage * image = [UIImage imageNamed:@"testImage.jpg"];
//创建一个UIImageView显示该图片
UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
//设置其内容在水平和垂直方向的压力为501
[imageView setContentCompressionResistancePriority:499 forAxis:UILayoutConstraintAxisHorizontal];
[imageView setContentCompressionResistancePriority:499 forAxis:UILayoutConstraintAxisVertical];
[self.view addSubview:imageView];
//创建距离父视图左边间接约束
NSArray * c1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c1];
//创建距离父视图上边间接约束
NSArray * c2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c2];
//创建视图宽带和高度的约束,并设置其约束力为500小于501
NSLayoutConstraint * c3 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c3.priority = 500;
[self.view addConstraint:c3];
NSLayoutConstraint * c4 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c4.priority = 500;
[self.view addConstraint:c4];
``` 运行效果如下图所示
内容驱动布局是AutoLayout的核心优势,通过这样的机制,可以不写代码,便实现不同内容的显示模式下,自动平衡视图组件的尺寸。
CGSize size = screen.bounds.size;
CGPoint orgion = CGPointMake(0, 0);
CGRect frame;
frame.origin = orgion;
frame.size = size;
UIWindow * window = [[UIWindow alloc]initWithFrame:frame];
[window makeKeyAndVisible];
确定UIWindow位置和大小的参考系就是屏幕参考系。也可以说,window对象的位置是基于屏幕。
接下来我们继续研究非UIWindow的视图对象的参考系。因非UIWindow的视图对象需要加入视图树中才能够被显示,也就是需要作为已经显示在屏幕上的视图的子视图。
能够在屏幕上看到的视图对象一定有一个父视图,这个父视图对象便是子视图的参考系。默认情况下,参考系的原点为父视图的左上角顶点,参考系的边界为父视图的边界。但此默认情况可以通过配置bounds属性调整。
UIView * redView = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
表示创建了一个视图,这个视图的左上角顶点,距离父视图左上角顶点偏移了(50,50),自身的大小为100×100。此时无法确定该视图在屏幕上的位置,因还不知道父视图在屏幕上的位置。所有这里的frame属性仅仅是基于父视图的偏移量。
如果子视图自身大小超出了父视图的边界,也就是参考系边界,那么超出部分是否显示由父视图决定,通过父视图的clipsToBounds属性
// When YES, content and subviews are clipped to the bounds of the view.
//Default is NO.
@property(nonatomic) BOOL clipsToBounds;
bounds属性
上文中说,视图参考系默认情况下是父视图提供的,其原点为父视图的左上角顶点,参考系边界为父视图边界。但也可以通过配置bounds属性的值来调整参考系原点和边界大小。
// default bounds is zero origin, frame size.
@property(nonatomic) CGRect bounds;
在配置视图的frame属性时,bounds属性也会自动变换,通过注释可以看出,其origin值为zero,size值为frame中的size值。
bounds的origin主要是改变子视图参考系原点的位置。默认情况下origin为(0,0)表示:父视图左上角顶点是子视图参考系的原点。 但如果通过代码改变其值为(10,10),那么父视图左上角顶点将不再是子视图参考系的原点,而是(10,10)点,原点向上移动10个点,向左移动10 个点。这是,子视图的frame没有变换,但是父视图参考系发送变换,同样会引起子视图的位置改变。这个属性的使用场景这里暂不展开讲解。在之后章节中, 会进行深入介绍。
同样,我们可以通过bounds的size值来改变提供子视图参考系的边界大小。通常开发中不会主动调整此值,以免带来未知问题,例如按钮不能点击等,因为参考系边界不仅和现实相关还与时间传递有关,这里暂不深入。
2.spring模式
spring模式是位置的一种补偿模式。通过上文中的strut模式可以确定一个子视图在父视图中的固定偏移量。但是再实际使用时,常常会出现父视 图大小会发生改变的情况。比如App有竖屏模式转为横屏模式,此时如果不重新调整子视图相对于父视图的偏移量,将会出现现实错误。例如下图:
这是便需要根据父视图参考系的变化情况,提供一个动态的补偿机制,spring便是提供补偿机制的模式。
补偿机制是双休的,所以需要父视图的参考系发生变化时告知子视图,同时子视图需要根据变化做出相应的响应。
父视图通过autoresizesSubviews属性值来判断,是否告知子视图,本身的参考系发生变化。
// default is YES.
//if set, subviews are adjusted according to their autoresizingMask if self.bounds changes
@property(nonatomic) BOOL autoresizesSubviews
子视图通过autoresizingMask属性值做出相应的响应。
// simple resize. default is UIViewAutoresizingNone
@property(nonatomic) UIViewAutoresizing autoresizingMask;
UIViewAutoresizing为响应情况枚举:
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
autoresizingMask的默认值为UIViewAutoresizingNone,表示子视图的位置和大小按照当前在父视图参考系中的比例进行调整。调整方式如下图所示:
如果将autoresizingMask的值设置为枚举值中内容,那么该内容表示的尺寸将不做调整,例如UIViewAutoresizingFlexibleWidth表示,子视图本身的宽带将不会随父视图参考系变化二变化,如图所示
spring模式下的调整只能进行按比例缩放和固定,无法根据具体的要求进行补偿,现在基本不再使用此模式。
3.Auto Layout
AutoLayout模式是iOS6 SDK所支持的新机制。使用AutoLayout可以在不久层次结构中表示一个视图与自身、父视图、兄弟视图的关系。这些关系可以包含等式关系和不等式关 系,也可能涉及视图的属性,例如位置、范围。这些特性使AutoLayout能够以一种spring模型无法实现的方式实现精妙的边界条件。举例来说,你 可能会说“我想让这些等尺寸的视图排成一行,但是和父视图的边界距离不得小于20个点”或者“如果水平空间不足,我想在任何右边按钮受到影响前,让左边按 钮的文本先被裁减”。
代码中默认使用strut和spring模式。但从对spring和AutoLayout的功能描述上来看,两个功能明显冲突,都是对视图的布局进 行调整,spring是根据固定模式进行调整,而AutoLayout是需要根据约束进行求解。所有在同一个视图对象上,只能使用两种模式中的一共。需要 手动关闭strut和spring模式,才能使用AutoLayout模式。
关闭strut和spring的方法是通过视图对象的一个方法,通过设置flag为NO值,关闭strut和spring布局模式。
- (void)setTranslatesAutoresizingMaskIntoConstraints:(BOOL)flag
在AutoLayout模式下,每个视图对象在屏幕上的位置都是通过约束和内容压力求解出来的。
3.1约束
约束就是视图几何关系的限制条件,主要可以分成三类:
自身约束:宽度约束、高度约束
相对父视图约束:视图四边与父视图四边的距离、视图水平中心与父视图水平中心的距离、视图垂直中心与父视图垂直中心的距离。
兄弟视图间约束:水平间距,垂直间距,边缘对其,中心对其,尺寸匹配
兄弟视图定义:拥有相同父视图的视图对象。
创建约束的方法有三种:
使用生成约束的方法创建约束
使用约束描述字符串生成约束
使用Interface Builder添加约束
首先我们研究通过方法创建约束
UIView * view1;
UIView * view2;
NSLayoutAttribute att1;
NSLayoutAttribute att2;
CGFloat multiplier;
CGFloat constant;
NSLayoutRelation relation;
NSLayoutConstraint * constraint = [NSLayoutConstraint constraintWithItem:view1 attribute:att1 relatedBy:relation toItem:view2 attribute:att2 multiplier:multiplier constant:constant];
NSLayoutAttribute为属性枚举参数:
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
Left,Right,Top,Bottom为视图的四个边框
Leading,Trailing为视图的前边和后边。此处前边和后边是针对阅读顺序而言,对用中文,英文类的语言,左边就是前边,右边就是后边, 而对用阿拉伯文等从右向左阅读的文字,前边就是右边,后边就是左边。通常建议使用前后做约束,避免使用左右,这样可以更好的进行国际化的适配。
Width,Height表示视图自身的宽高属性
CenterX,CenterY依次表示视图自身水平中心和垂直方向的中心
Baseline为显示文字类组件特有的属性,表示文字显示的基线
NSLayoutRelation为关系枚举参数
NSLayoutRelationLessThanOrEqual = -1,
NSLayoutRelationEqual = 0,
NSLayoutRelationGreaterThanOrEqual = 1,
分别可以设置等于,小于等于,大于等于。这里在求解约束时,不等式较难理解。通常换一个角度去理解这个不等式,比如小于等于意思就是设置最大值,大于等于的意思就是设置最小值。
这段代码演示了创建约束的通用方式,其约束规定的内容为view1.attr1 关系 view2.attr2 * multiplier + constant。
下面我们依次举3个列子来讲解如何创建约束:
创建一个viewA宽度为10个点的约束
创建一个viewA的左边界距离父视图左边界20个点的约束
创建一个viewA的右边界距离viewB的右边界30个点的约束
UIView * fatherView = [[UIView alloc]initWithFrame:CGRectZero];
fatherView.translatesAutoresizingMaskIntoConstraints = NO;
UIView * viewA = [[UIView alloc]initWithFrame:CGRectZero];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewA];
UIView * viewB = [[UIView alloc]initWithFrame:CGRectZero];
viewB.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewB];
//创建一个viewA宽度为10个点的约束
NSLayoutConstraint * constraint_viewA_width = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:10];
//装载约束到父视图上
[fatherView addConstraint:constraint_viewA_width];
创建一个viewA的左边界距离父视图左边界20个点的约束
NSLayoutConstraint * constraint_superView_20_viewA = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:fatherView attribute:NSLayoutAttributeLeading multiplier:0 constant:20];
//装载约束到父视图上
[fatherView addConstraint:constraint_superView_20_viewA];
//创建一个viewA的右边界距离viewB的右边界30个点的约束
NSLayoutConstraint * constraint_viewA_30_viewB = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:viewB attribute:NSLayoutAttributeLeading multiplier:0 constant:30];
//装载约束到父视图上
[fatherView addConstraint:constraint_viewA_30_viewB];
>代码中用NSLayoutAttributeLeading表示左边,NSLayoutAttributeTrailing表示右边
>视图对象的约束一定要装载到其父视图身上,视图对象的自我约束力不够,需要父视图管理才可以。
接下来我们继续研究通过约束描述字符串生成约束用字符串主要描述一下两点
* 边界之间的间距
* 本身宽高
设计一下3个关键符号
* `| `表示父视图边界,根据字符串语义判定哪个边界
* `[] `表示视图对象,例如`[aView] `表示aView视图对象,通过一个`@{@"aView":aView} `字典进行绑定
* `-x- `表示距离,x为数组或字符,例如`-20- `或者`-tempX- `,通过一个`@{@"tempX":@20} `字典对字符串中的tempX进行数值绑定。
* `H: `表示水平方向
* `V: `表示垂直方向
举例:
* `H:[aView(100)] `表示aView的宽带为100
* `H:|-20-[aView] `表示aView的左边界距离父视图的左边界为20个点。
* `V:[aView]-10-| `表示aView的下边界距离父视图的下边界为10个点。
* `V:[aView(30)] `表示aView的高度为30
采用字符串描述约束的核心优势是可以同时描述多个视图对象
* `H:|-20-[aView]-40-[bView(==aView)]-20-| `表示,aView和bView等宽,其间距40,分别距离父视图两边20。
* `V:|-20-[aView(40)][bView(50)] `表示,aView高度40,距离父视图上边界20,bView高度50紧贴aView下边。
通过NSLayoutConstraint的constraintsWithVisualFormat方法将描述约束的字符串生成约束对象。
NSString * constraint_string = @"|-x-[viewA(20)]";
NSDictionary merticsDic = @{@"x":@10}; NSDictionary viewsDic = @{@"viewA":viewA};
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraint_string options:NSLayoutFormatDirectionLeadingToTrailing metrics:merticsDic views:viewsDic];
[fatherView addConstraints:constraints];
>方法中options参数是枚举值,主要描述文字的阅读方式是从左到右,还是从右到左,通常选择NSLayoutFormatDirectionLeadingToTrailing表示根据语言环境决定从前到后。
>
NSLayoutFormatDirectionLeadingToTrailing, NSLayoutFormatDirectionLeftToRight, NSLayoutFormatDirectionRightToLeft,
>方法中的metrics是一个字典,字典中的key为字符串中描述数值的字符,value为该字符对应的真实值。
>方法中的views是一个字典,字典中的key为字符串中描述视图对象的字符,value为该字符对应的真实对象。为了方便创建此类型字典UIKit中提供一个宏方便创建字典`NSDictionaryOfVariableBindings(<#...#>) `。
>
NSDictionary * dic = NSDictionaryOfVariableBindings(aView); 相当于@{@"aView":aView}字典
>因一个字符串中可以描述多个约束,所以返回一个数组,数组中为此字符串描述的所有约束对象。
>通过调用父视图的addConstraints方法,一次装载多个约束。
下面我们重复上节的3个列子来演示如何用描述字符串创建约束
* 创建一个viewA宽度为10个点的约束
* 创建一个viewA的左边界距离父视图左边界20个点的约束
* 创建一个viewA的右边界距离viewB的右边界30个点的约束
UIView * fatherView = [[UIView alloc]initWithFrame:CGRectZero];
fatherView.translatesAutoresizingMaskIntoConstraints = NO;
UIView * viewA = [[UIView alloc]initWithFrame:CGRectZero];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewA];
UIView * viewB = [[UIView alloc]initWithFrame:CGRectZero];
viewB.translatesAutoresizingMaskIntoConstraints = NO;
[fatherView addSubview:viewB];
//创建绑定字典
NSDictionary * views = NSDictionaryOfVariableBindings(fatherView,viewA,viewB);
//创建一个viewA宽度为10个点的约束描述字符串
NSString * constraintString1 = @"H:[viewA(10)]";
//通过字符串生成约束
NSArray * constraints1 = [NSLayoutConstraint constraintsWithVisualFormat:constraintString1 options:0 metrics:nil views:views];
//装载约束到父视图上
[fatherView addConstraints:constraints1];
//创建一个viewA的左边界距离父视图左边界20个点的约束
NSString * constraintString2 = @"H:|-20-[viewA]";
//通过字符串生成约束
NSArray * constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:constraintString2 options:0 metrics:nil views:views];
//装载约束到父视图上
[fatherView addConstraints:constraints2];
//创建一个viewA的右边界距离viewB的右边界30个点的约束
NSString * constraintString3 = @"H:[viewA]-30-|";
//通过字符串生成约束
NSArray * constraints3 = [NSLayoutConstraint constraintsWithVisualFormat:constraintString3 options:0 metrics:nil views:views];
//装载约束到父视图上
[fatherView addConstraints:constraints3];
>通过对比可以看出,通过描述字符串生成约束的方法更加简单直观。当然,更简单的方式是通过IB(Interface Builder)添加约束,关于IB的使用,请参加视频教程。
**3.2内容压力**
---
内容压力可以影响装载内容的对象外形尺寸。这里用UIImageView对象举例子,其他装载内容的视图对象用法一致。
在AutoLayout模式下,内容可以对组件边界产生两种压力
* 正压力:迫使组件改变自身的大小来显示完整的内容。
* 负压力:迫使组件维持当前的大小来显示完整内容。
这样描述比较抽象,举个例子:
现在有一张200×200的图片需要用一个UIImageView视图对象来显示。
**正压力**
如果我这是UIImageView视图对象的宽高都为100,那么内容会产生正压力,迫使视图的宽高都变为200,这样才能完整显示图片内容。
**负压力**
如果我这是UIImageView视图对象的宽高都为300,那么内容会产生负压力(吸引力),迫使视图的宽高都变为200,这样才能完整显示图片内容,而不留空白。
内容产生的压力能否是视图尺寸发送改变取决于以下两个因素:
* 压力的大小
* 约束力的大小
>这里的大小在代码中用优先级来描述,优先级高的代表力量大,在求解时起主导作用。每一个约束都可以配置其优先级,同时也可以配置内容压力的优先级。
压力的优先级通过两个方法分别进行配置
//设置正压力的大小 [viewA setContentCompressionResistancePriority:<#(UILayoutPriority)#> forAxis:<#(UILayoutConstraintAxis)#>]
//设置负压力的大小 [viewA setContentHuggingPriority:<#(UILayoutPriority)#> forAxis:<#(UILayoutConstraintAxis)#>]
>设置压力时,需要通过Axis方法参数制定水平还是数值方向,UILayoutConstraintAxis是枚举类型,其枚举值为UILayoutConstraintAxisHorizontal和UILayoutConstraintAxisVertical。
>压力优先级为CGFloat类型,范围是1-1000。其中系统给定了几个默认值:
>
UILayoutPriorityRequired=1000
UILayoutPriorityDefaultHigh=750
UILayoutPriorityDefaultLow=250
UILayoutPriorityFittingSizeLevel=50
约束力的优先级通过约束的`priority `配置,在约束装载到视图之前可以任意更改优先级,一旦,约束装载到了视图上,约束的优先级便不可再更改,如果需要更该,需要删除之前的约束,重新创建。
@property UILayoutPriority priority;
其类型与压力优先级一致。
下面我们通过代码演示一下内容压力和约束力直接的较量。还是通过本节一开始的例子,一个图片大小为200×200。一个UIImageView的宽高约束为100×100。此时,UIImageView对象的最终大小由内容压力和约束较量之后决定。
如果内容压力大于约束力,最终UIImageView的大小为200×200
//获取一个图片对象,该图片大小为200×200
UIImage * image = [UIImage imageNamed:@"testImage.jpg"];
//创建一个UIImageView显示该图片
UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
//设置其内容在水平和垂直方向的压力为501
[imageView setContentCompressionResistancePriority:501 forAxis:UILayoutConstraintAxisHorizontal];
[imageView setContentCompressionResistancePriority:501 forAxis:UILayoutConstraintAxisVertical];
//将视图对象贴在视图控制器的根视图上
[self.view addSubview:imageView];
//创建距离父视图左边间接约束
NSArray * c1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c1];
//创建距离父视图上边间接约束
NSArray * c2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c2];
//创建视图宽带和高度的约束,并设置其约束力为500小于501
NSLayoutConstraint * c3 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c3.priority = 500;
[self.view addConstraint:c3];
NSLayoutConstraint * c4 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c4.priority = 500;
[self.view addConstraint:c4];
运行效果如下图所示
![](/fileserve/userimg/201506/0e79989c1a1a11e583b097589ede9018.png)
如果内容压力小于约束力
self.view.backgroundColor = [UIColor whiteColor];
//获取一个图片对象,该图片大小为200*200
UIImage * image = [UIImage imageNamed:@"testImage.jpg"];
//创建一个UIImageView显示该图片
UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
//设置其内容在水平和垂直方向的压力为501
[imageView setContentCompressionResistancePriority:499 forAxis:UILayoutConstraintAxisHorizontal];
[imageView setContentCompressionResistancePriority:499 forAxis:UILayoutConstraintAxisVertical];
[self.view addSubview:imageView];
//创建距离父视图左边间接约束
NSArray * c1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c1];
//创建距离父视图上边间接约束
NSArray * c2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-50-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)];
[self.view addConstraints:c2];
//创建视图宽带和高度的约束,并设置其约束力为500小于501
NSLayoutConstraint * c3 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c3.priority = 500;
[self.view addConstraint:c3];
NSLayoutConstraint * c4 = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:100];
c4.priority = 500;
[self.view addConstraint:c4];
``` 运行效果如下图所示
内容驱动布局是AutoLayout的核心优势,通过这样的机制,可以不写代码,便实现不同内容的显示模式下,自动平衡视图组件的尺寸。