iOS开发之 自动布局

iOS开发之自动布局AutoLayout

目录:

1 iOS自动布局简介
2 iOS自动布局AutoLayout(代码)
3 iOS自动布局之VFL
4 iOS自动布局之Xib
5 自动布局开源库Masonry、PureLayout
6 约束动画

iOS自动布局简介 AutoLayout


自动布局背景

在iOS6之前,布局是通过UI控件的Frame属性和Autoresizing Mask来进行UI布局的。AutoLayout则是苹果公司在iOS6推出的一种基于约束的,描述性的布局系统。尤其当iPhone6机型尺寸退出后,自动布局就被越来越多的人所应用。

自动布局重要概念

1 约束:对控件位置和大小的限定条件
2 参照:对控件位置的约束是相对于哪一个视图而言的

自动布局的核心计算公式

obj1.property1 = (obj2.property2 * multiplier) + constant value
tips:obj1的property1属性等于obj2的property2属性乘以multiplier(系数) 再加 constant (常量)。

约束的优先级

约束的priority 属性表示约束的优先级,取值区间在[0,1000],默认是1000,priority值越大,表示优先级越高,会优先执行,优先级低的约束会在优先级高的约束执行完成之后执行。

约束警告和错误:

在使用storyboard或者xib的时候,约束可能变成黄色的警告或者红色的错误时,说明约束设置的有问题
1 警告:表示控件的frame和设置的约束不符,更新frame或者更新约束进行解决。
2错误:表示约束设置不完全或者约束之间有冲突,这时只有把约束设置完全或者解决冲突才能解决错误。

添加约束的三条规则:

对于两个同层级View之间的约束关系,应该添加到它们的父View之上。


对于两个不同层级View之间的约束关系,应该添加到它们最近的共同父View上。

对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上


代码实现AutoLayout

1 先禁用view的autoresizing功能,设置属性为NO
view.translatesAutoresizingMaskIntoConstraints = NO;
2 添加约束之前,保证view添加到了对应的父控件上
3 使用了AutoLayout就不要考虑frame了。
4 NSlayoutConstraint: 一个NSlayoutConstraint就代表一个约束对象。

/**
 *  添加一个约束,其实就是根据公式来计算约束
 *  obj1.property1 =(obj2.property2 * multiplier)+ constant value
 *  @param view1      需要添加约束的View
 *  @param attr1      需要添加的约束(左边、右边、长宽还是。。。)
 *  @param relation   约束关系(大于、等于还是小于)
 *  @param view2      参照View(约束是相对于哪个View而言)
 *  @param attr2      参照View的哪一个参数(左边、右边、长宽还是。。。)
 *  @param multiplier 系数
 *  @param c          常量值
 *
 *  @return 返回一个创建好的约束
 */

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

5 添加约束的时候,一定要确定添加到对应的item上。

一般view自身的约束包括(宽度、高度、宽高比等)
view之间或者父子view之间的约束,都应该添加到公共父view上



VFL添加约束 (Visual Format Language)

苹果提供的另外一种通过代码添加约束的方式,官方提供了一些资料,但是也不是很全,严格意义上将VFL并不是一门语言,感觉像是一种解析方式,必须按照定义的规则来编写约束,目前还有很多坑,不建议使用。

1 使用VFL创建约束数组:

/**
*  使用VFL语句来创建约束数组
*
*  @param format  VFL语句
*  @param opts    约束类型
*  @param metrics VFL语句中用到的具体数值
*  @param views   VFL语句中用到的控件
*
*  @return 返回创建好的约束数组
*/
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary *)metrics views:(NSDictionary *)views;

2 VFL语句示例

H:[cancelButton(72)]-12-[acceptButton(50)]
canelButton宽72,acceptButton宽50,它们之间间距12

H:[wideView(>=60@700)]
wideView宽度大于等于60point,该约束条件优先级为700(优先级最大值为1000,优先级越高的约束越先被满足)

V:[redBox][yellowBox(==redBox)]
竖直方向上,先有一个redBox,其下方紧接一个高度等于redBox高度的yellowBox

H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|
水平方向上,Find距离父view左边缘默认间隔宽度,之后是FindNext距离Find间隔默认宽度;再之后是宽度不小于20的FindField,它和FindNext以及父view右边缘的间距都是默认宽度。(竖线“|” 表示superview的边缘)

3 VFL代码示例

    NSString* vfl = @"H:|-margin-[imageV]-margin-|";//水平约束
    NSDictionary* metrics = @{@"margin":@40};//添加约束时会自动解析这个字典
    NSDictionary* views = NSDictionaryOfVariableBindings(imageV);
    NSArray* arrayH = [NSLayoutConstraint constraintsWithVisualFormat:vfl options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views];
    [self.view addConstraints:arrayH];
    
    
    NSString* vfl1 = @"V:|-100-[imageV(150)]";//高度
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1 options:NSLayoutFormatAlignAllCenterY metrics:nil views:views]];

4效果图

5 学习的地址:
http://www.jianshu.com/p/385070898e77
http://blog.csdn.NET/a1152024140/article/details/40823883
http://www.cocoachina.com/industry/20131108/7322.html


可视化实现自动布局之Xib&SB:

前面通过代码实现自动布局可以发现,如果要通过自动布局编写一个复杂的页面,用代码实现的话代码量太大,而且可读性很差,还好苹果为我们提供了一种可视化添加约束的自动布局方式,大大提升了页面布局的效率。



1 可视化布局提供了苹果UIKit中大部分控件,直接拖拽到对应的view上就可以进行自动布局了。

2 给对应的控件添加约束 (实例:宽度240 高度128,水平垂直居中)

3 效果(居中,高度和宽度约束)


自动布局开源库


Mansory

Mansory 是目前最流行的Autolayout第三方框架,让你可以用简单的代码来编写Autolayout,省去了苹果官方繁复的Autolayout代码,大大提升了开发效率。相对于PureLayout(为UIView添加分类) 来说 Mansory 拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了 并具有高可读性,而且同时支持iOS和Max OS X。Mansory github地址:https://github.com/SnapKit/Masonry

1 官方sample code:

 [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(superview).with.insets(padding);
 }];

可以看到Mansory布局的代码方式通过 block实现,看起来代码比较集中和清晰,然后:make edges equalTo superview with insets 。这种通过链式的自然语言,把约束设置好了,看起来很简单。

2 Mansory提供的属性

@property (nonatomic, strong, readonly) MASViewAttribute *left;
 @property (nonatomic, strong, readonly) MASViewAttribute *top;
 @property (nonatomic, strong, readonly) MASViewAttribute *right;
 @property (nonatomic, strong, readonly) MASViewAttribute *bottom;
 @property (nonatomic, strong, readonly) MASViewAttribute *leading;
 @property (nonatomic, strong, readonly) MASViewAttribute *trailing;
 @property (nonatomic, strong, readonly) MASViewAttribute *width;
 @property (nonatomic, strong, readonly) MASViewAttribute *height;
 @property (nonatomic, strong, readonly) MASViewAttribute *centerX;
 @property (nonatomic, strong, readonly) MASViewAttribute *centerY;
 @property (nonatomic, strong, readonly) MASViewAttribute *baseline;
 @property (nonatomic, strong, readonly) MASViewAttribute *(^attribute)(NSLayoutAttribute attr);

 #if TARGET_OS_IPHONE

 @property (nonatomic, strong, readonly) MASViewAttribute *leftMargin;
 @property (nonatomic, strong, readonly) MASViewAttribute *rightMargin;
 @property (nonatomic, strong, readonly) MASViewAttribute *topMargin;
 @property (nonatomic, strong, readonly) MASViewAttribute *bottomMargin;
 @property (nonatomic, strong, readonly) MASViewAttribute *leadingMargin;
 @property (nonatomic, strong, readonly) MASViewAttribute *trailingMargin;
 @property (nonatomic, strong, readonly) MASViewAttribute *centerXWithinMargins;
 @property (nonatomic, strong, readonly) MASViewAttribute *centerYWithinMargins;

经常用NSLayoutConstraint编写约束的同学可能感觉比较熟悉,这里的属性的命名方式和NSLayoutConstraint类下的NSlayoutAttribute枚举一致,这样你就会发现,Masonry其实就是把我们系统的NSLayoutConstraint类等Aotulayout布局相关进行了封装,曝露出比较简单易懂(链式的自然语言)的Aotulayout接口。

typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
 NSLayoutAttributeLeft = 1,
 NSLayoutAttributeRight,
 NSLayoutAttributeTop,
 NSLayoutAttributeBottom,
 NSLayoutAttributeLeading,
 NSLayoutAttributeTrailing,
 NSLayoutAttributeWidth,
 NSLayoutAttributeHeight,
 NSLayoutAttributeCenterX,
 NSLayoutAttributeCenterY,
 NSLayoutAttributeBaseline,
 NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline,
 NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0),

 NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
 NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),

 NSLayoutAttributeNotAnAttribute = 0
 };

3 Mansory示例代码

UIImageView* imageV = [[UIImageView alloc]init];
    imageV.image = [UIImage imageNamed:@"img_tip"];
    imageV.contentMode = UIViewContentModeScaleAspectFill;
    imageV.clipsToBounds = YES;
    [self.view addSubview:imageV];
    
    __weak __typeof (&*self)weakSelf = self;
    [imageV mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(weakSelf.view).offset(40);
        make.centerY.equalTo(weakSelf.view);
        make.right.mas_equalTo(weakSelf.view).offset(-40);
        make.height.equalTo(imageV.mas_width * 3.0/4);
    }];
    
    UILabel* titleL = [[UILabel alloc]init];
    titleL.text = @"Masonry自动布局";
    titleL.textAlignment = NSTextAlignmentCenter;
    titleL.font = [UIFont systemFontOfSize:15];
    titleL.textColor = [UIColor blackColor];
    [self.view addSubview:titleL];
    
    [titleL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(imageV);
        make.bottom.equalTo(imageV.mas_top).with.offset(-20);
    }];

4 效果图

可以看到,Mansory虽然也谢了不少代码,但是看起来简单易懂,在实际编写约束中,可以用Mansory作为xib的辅助工具,编写一些动态的约束。

tips:

1 Mansory添加约束的三个函数

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
 - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
 - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;

2 Mansory的一些小细节

mas_equalToequalTo: 默认情况下mas_equalTo有自动包装功能,比如自动将20包装为@20。equalTo没有自动包装功能。如果添加了下面的宏,那么mas_equalTo和equalTo就没有区别:

// 注意:这个宏一定要添加到#import "Masonry.h"前面
#define MAS_SHORTHAND_GLOBALS

mas_widthwidth : 默认情况下
width是make对象的一个属性,用来添加宽度约束用的,表示对宽度进行约束。mas_width是一个属性值,用来当做equalTo的参数,表示某个控件的宽度属性。如果添加了下面的宏,mas_width也可以写成width:

#define MAS_SHORTHAND

mas_height、mas_centerX以此类推。


自动布局第三方库之 PureLayout

相对于Mansory来说,PureLayout就是一个轻量级的自动布局框架,没有自己再去封装一层语法,而是直接通过为UIView添加分类的方式实现,内部也是直接调用NSLayoutConstraint添加约束。

1 官方示例代码
下面是通过PureLayout给view添加约束(view1的顶部等于view2的底部)

[view1 autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:view2]

如果不使用PureLayout,下面是通过使用Apple's Foundation API 达到相同的效果:

[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];

很多PureLayout的APIS默认帮你创建了多个约束(约束数组),帮助你编写可读性高的布局代码。


// 2 constraints created & activated in one line!(创建了水平居中和垂直居中两个约束)
logoImageView.autoCenterInSuperview()

// 4 constraints created & activated in one line!(创建上左下右约束数组)
textContentView.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsets(top: 20.0, left: 5.0, bottom: 10.0, right: 5.0))

PureLayout总是返回你创建的约束/约束数组,方便你进行操作:

//返回单个约束
NSLayoutConstraint* constraint = [view1 autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:view2];
//返回约束数组
NSArray* constraints = [view1 autoSetDimensionsToSize:CGSizeMake(100, 100)];

PureLayout supports all Auto Layout features including inequalities, priorities, layout margins, identifiers, and much more. It's a comprehensive, developer-friendly way to use Auto Layout.


2 PureLayout Demo:

UIImageView* imageV = [[UIImageView alloc]init];
    imageV.image = [UIImage imageNamed:@"img_tip"];
    imageV.contentMode = UIViewContentModeScaleAspectFill;
    imageV.clipsToBounds = YES;
    [self.view addSubview:imageV];
    
    [imageV autoCenterInSuperview];
    [imageV autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:40];
    [imageV autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:40];
    [imageV autoMatchDimension:ALDimensionHeight toDimension:ALDimensionWidth ofView:imageV withMultiplier:0.75];
    
    
    UILabel* titleL = [[UILabel alloc]init];
    titleL.text = @"PureLayout自动布局";
    titleL.font = [UIFont systemFontOfSize:17];
    titleL.textColor = [UIColor blackColor];
    titleL.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:titleL];
    
    [titleL autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:imageV withOffset:-20];
    [titleL autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:imageV];
    [titleL autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:imageV];


3效果图

约束动画

在执行动画时记得调用以下方法:

//在修改了约束之后,只要执行下面代码,约束就能做出动画效果
[UIView animateWithDuration:0.5 animations:^{
      [self.view layoutIfNeeded];
  }];






送上本文所有demo的地址:

1、UITableView-FDTemplateLayoutCell   自动计算布局

2、masonary进行自动布局,然后再实现自动计算cell行高

问题解疑:

1、preferredMaxLayoutWidth
    1)为什么设置这个属性: 首先UILabel显示几行跟label的最大布局宽度有关
    2)storyboard:
        1)默认没有勾选这个选项,因为系统会自动计算(但是官方说只在ios8
                                    之后有用,所以ios7没用)
        2)勾选:有时候会发现勾选后的值不对,所以建议不要管
        3)在代码中设置(最靠谱)
 3)iOS7以及以下的操作系统上,UILabel显示多行文本是又不足的,你需要设置UILabel的
            preferredMaxLayoutWidth为一个固定值才能显示多行文本。在iOS8以后就不再需要设置这个了
4)根据实践结果,很多时候UILabel多行不适配的时候就是这个属性设置错误或者
                没有设置导致的,尽量去设置。

2、算出autolayout的最终高度,但是返回高度需要加1.0
      1)原因:由于cell分割线的问题,cell的高度比contentview的高度多1,所以需要加上

3、content Hugging(通俗来讲就是防止控件变大)和 content Compression Resistance(防止控件变小)
    1)这两个属性是针对有intrinsic size属性的控件,也就是拥有intrinsic size属性的控件知道自己(自己可以计算)的大小,例如一个lable,知道自己的text,和font就可以自己计算自己的大小
    2)对应代码中的API(就是设置优先级)
        contentCompressionResistancePriorityForAxis
        setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis 
        contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis
        setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis
        
    
3)Hugging priority确定view有多大的优先级组织自己变大,,Compression Resistance priority确定有多大的优先级阻止自己变小,其实content Hugging就是维持当前view在它的optimal size(intrinic size),相当于给view添加一个width约束,这个约束防止view变大。content Hugging优先级默认是250,Content Compression就是维持view在他的optimal size(intrinic size),相当于添加一个width约束,防止view变小,compression priority默认是750

重点:(设置优先级就是要先保证优先级高的约束)

你可能感兴趣的:(iOS开发之 自动布局)