来自Leo的原创博客,转载请著名出处

我的StackOverflow


前言

AutoLayout用来布局已经不是什么新鲜事了,我之前也写过三篇入门的文章

  • IB 自动生成
  • IB拖拽
  • 代码实现

当然,实际开发中,如果是多人协同开发,基本上是弃用Storyboard和XIB文件的。因为这两种文件本质上是XML,多人协同开发一起修改这样的大XML是一件很蛋疼的事情。

所以,大部分时候,Layout约束都是纯代码实现的。

除非某些公司要实现自己的Layout引擎。否则,更多的还是使用第三方库

  • Masonry

使用Masonry能够让约束创建变得很容易。但是,为了更好的利用AutoLayout,很多约束相关的基础知识要了解,这样能够帮助你实现更复杂更灵活的布局。


约束优先级

NSLayoutConstraint有个属性是priority,类型是UILayoutPriority,本质上其实是float类型

typealias UILayoutPriority = Float

可以去取如下值,当然也可以直接设置float值

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

用处:当两个约束发生冲突的时候,会break优先级低的约束

举个例子先,

例如,我希望有这样的一个View

  • 水平垂直居中
  • 宽度320,高度200
  • 距离左右的最小间隔大于等于20(为了适配小屏幕)

这时候,添加的约束如下

然后,我们看看预览

在4s上,预览是不正确的,但是在大屏上,一切正常

如果这时候运行4s模拟器,会发现Log

2016-06-03 23:01:40.339 OCTest[1004:26277] Unable to simultaneously satisfy constraints.Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it.
("<NSLayoutConstraint:0x79159c40 H:|-(>=20)-[UIView:0x79159c70]   (Names: '|':UIView:0x79158e50 )>","<NSLayoutConstraint:0x79159dc0 UIView:0x79159c70.centerX == UIView:0x79158e50.centerX>","<NSLayoutConstraint:0x79159e50 H:[UIView:0x79159c70(320)]>","<NSLayoutConstraint:0x79144ba0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x79158e50(320)]>"
)Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x79159e50 H:[UIView:0x79159c70(320)]>Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

原因不难发现,

  • 4s上屏幕宽度是320,如果要保证View的左右距离边框大于等于20,那么View的宽度小于等于280
  • View还有个约束是宽度为320

这就是优先级的作用了,我们设置宽度的优先级更低,这样在有约束和宽度发生冲突的时候,默认break宽度约束

在IB上,可以这么设置
由于默认的priority是1000,这里我们只需要将其设置为999就可以了

然后,预览和运行后都正常了

Tips:在IB上,如果你看到一个约束的是虚线表示的时候,那么这个约束的优先级是小于默认的


Intrinsic Size(内在大小)

我们知道,UILabel和UIButton在设置约束的时候,只需要确定位置就足矣了,而不需要设置大小的约束。因为,UILabel和UIButton有内在大小

内在大小是UIView的一个方法,也就是说,你可以通过继承,为UIView及其子类返回自定义的内在大小

-(CGSize)intrinsicContentSize{return [super intrinsicContentSize];
}

一个很典型的例子,假如我只为一个UIView设置水平和垂直居中

这时候,你会发现,约束是红色的,也就是只确定了位置,没确定大小,autolayout无法确认view的状态

这时候,在IB上设置内置大小为300*200,设置内在大小的位置在下图最下一个选项 Intrinsic size

设置完以后,发现IB正常了。
这样设置并不会影响运行时,只会去除IB警告
所以,我们新建一个UIView子类,然后将该视图的类修改

@interface AutoAdjustView : UIView
@end
@implementation AutoAdjustView
-(CGSize)intrinsicContentSize{return CGSizeMake(300, 200);
}
@end

在运行发现一切正常。

Intrinsic Size有两个很常见的使用场景

场景一,为UILabe,UIButton等设置额外的Padding

举个例子,一个默认的UIButton,设置文字后的效果是这样子的

当我们写一个子类,重载intrinsicContentSize

@implementation RoundButton-(CGSize)intrinsicContentSize{CGSize size = [super intrinsicContentSize];size.width += size.height;return size;
}
-(void)layoutSubviews{[super layoutSubviews];self.layer.cornerRadius = ceil(self.bounds.size.height/2);self.layer.masksToBounds = YES;
}
@end

效果是是这样子的,可以看到,我们不管实际宽度多少,都添加了额外的padding。

场景二,让父视图根据子视图来自适应自己的大小(为了方便,我在IB上拖拽了,代码实现类似)
首先,拖拽一个背景色为蓝色的view作为父视图,然后父视图中添加一个label。

父视图的设置

  • 水平垂直居中
  • Intrinsic Size 为0,0

Label的设置

  • 距离top,bottom,leading,trailing,距离为20


这时候,预览如下(仅仅修改label文字)

可以看到,我们并没有设置蓝色视图的高度宽度,它可以自适应内部包含视图。MBProgressHUD的中间区域自适应大小就是这么做的。


Hugging Priority

UIView有一个方法是

setContentHuggingPriority: forAxis:

Sets the priority with which a view resists being made larger than its intrinsic size.(当View的大小大于内置大小的时候,抗拒变大的优先级,也就是说,优先级越大,抗拒能力越强)

其中,priority是UILayoutPriority,和上文的一致。Axis是坐标系

typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {UILayoutConstraintAxisHorizontal = 0,UILayoutConstraintAxisVertical = 1
};

就是,你可以分别设置水平垂直的优先级。

举个例子
IB拖拽红蓝两个View
蓝色设置如下

  • 距离顶部8
  • 距离左边20
  • 内置大小120*50

蓝色设置如下

  • 距离顶部8
  • 距离右面20
  • 内置大小120*50

这时候4.7英寸屏幕预览如下

接下来,添加两个View的水平间隔为10

可以看到,约束发生冲突了,因为两个view的抗拉伸优先级是一样的。

由于默认的优先级值是250,所以,我们选中蓝色View,设置优先级为249的时候,会拉伸蓝色自己,如果是251,则会拉伸红色


Compression Resistance Priority

Sets the priority with which a view resists being made smaller than its intrinsic size.也就是说和上文提到的Hugging Priority类似,只不过这个是抗压缩优先级,这里再举例子了。

- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis

systemLayoutSizeFittingSize

很多时候,比如UITableviewCell,比如UICollectionViewCell,我们需要知道当前视图内部子视图的最合适的的高度是多少。很幸运,UIView为我们提供了这个方法

systemLayoutSizeFittingSize

Returns the size of the view that satisfies the constraints it holds.The size of the view that satisfies the constraints it holds.也就是说,根据子视图的约束,按照fittings Size来返回最合适的大小。

其中fittings Size可选值如下

const CGSize UILayoutFittingCompressedSize;//满足约束的最可能小的大小
const CGSize UILayoutFittingExpandedSize;//满足约束的最可能大的大小

以TableViewCell高度计算为例

假如我们有这样的一个Cell

  • 子视图就一个Label,上下左右各具contentView距离为8

那么,如何在heightForRowAtIndexPath里返回高度呢?

方法一,根据Model计算
根据boundingRectWithSize: options: attributes: context:来计算高度,最后加上padding。-iOS 7之前常用,很土,但是通用的方法。

方法二,交给UIKit,自己去计算高度

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{return UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{return UITableViewAutomaticDimension;
}

方法三,用systemLayoutSizeFittingSize来计算

这里有个地方要注意,我们只有固定了宽度,才能计算出高度,所以,从下文代码里你能看到,我们添加了一个宽度约束小于等于屏幕宽度

注意:当有accessoryView的时候或者Table不是全屏,cell的宽度并不是屏幕宽度

@interface TableViewController ()@property (strong,nonatomic)CustomCell * sizeCell;@end@implementation TableViewController- (void)viewDidLoad {[super viewDidLoad];[self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:nil] forCellReuseIdentifier:@"cell"];self.sizeCell = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil].firstObject;CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;[self.sizeCell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.sizeCell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:screenWidth]];
}-(NSArray *)cellTexts{return @[@"dagudhau",@"dagudhau",@"daguddhaughduahhau",@"dagudhauhduahgudahughduahguhagudhauhfuadhgudhauhuadh",@"daghduahgudhaguhdauhguhaguhdughauudhau",
@"agudhauhduahgudahughduahguhagudhauhfuadhgudhauhuadhdaghduahgudhaguhdauhguhaguhdughauudhauagudhauhduahgudahughduahguhagudhauhfuadhgudhauhuadhdag",@"dagudhau",@"dagudhau",];
}
#pragma mark - Table view data source-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{self.sizeCell.customLabel.text = [self cellTexts][indexPath.row];CGSize fitSize = [self.sizeCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];return fitSize.height;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {return 1;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return [self cellTexts].count;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];cell.customLabel.text = [self cellTexts][indexPath.row];return cell;
}

效果如下
iphone 6

iphone 4s


AutoLayout进阶篇相关推荐

  1. Enterprise Library Step By Step系列(十二):异常处理应用程序块——进阶篇

    一.把异常信息Logging到数据库 在日志和监测应用程序块中,有朋友提意见说希望能够把异常信息Logging到数据库中,在这里介绍一下具体的实现方法. 1.创建相关的数据库环境: 我们可以用日志和监 ...

  2. Docker 数据卷之进阶篇

    Docker 数据卷之进阶篇 原文:Docker 数据卷之进阶篇 笔者在<Docker 基础 : 数据管理>一文中介绍了 docker 数据卷(volume) 的基本用法.随着使用的深入, ...

  3. C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码...

    原文:C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码 前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github. ...

  4. Kafka核心设计与实践原理总结:进阶篇

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! kafka作为当前热门的分布式消息队列,具有高性能.持久化.多副本备份.横向扩展能力.我学习了<深入理解K ...

  5. 计算机编程书籍-笨办法学Python 3:基础篇+进阶篇

    编辑推荐: 适读人群 :本书适合所有已经开始使用Python的技术人员,包括初级开发人员和已经升级到Python 3.6版本以上的经验丰富的Python程序员. "笨办法学"系列, ...

  6. 最快让你上手ReactiveCocoa之进阶篇

    前言 由于时间的问题,暂且只更新这么多了,后续还会持续更新本文<最快让你上手ReactiveCocoa之进阶篇>,目前只是简短的介绍了些RAC核心的一些方法,后续还需要加上MVVM+Rea ...

  7. SQL Server调优系列进阶篇(如何维护数据库索引)

    前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...

  8. mysql 开发进阶篇系列 10 锁问题 (使用“索引或间隙锁”的锁冲突)

    1.使用"相同索引键值"的冲突 由于mysql 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但如果是使用相同的索引键,是会出现锁冲突的.设计时要注意 例 ...

  9. Java进阶篇(一)——接口、继承与多态

    前几篇是Java的入门篇,主要是了解一下Java语言的相关知识,从本篇开始是Java的进阶篇,这部分内容可以帮助大家用Java开发一些小型应用程序,或者一些小游戏等等. 本篇的主题是接口.继承与多态, ...

最新文章

  1. 干货|你的Paper阅读能力合格了吗(硕士生版)
  2. 替代还是扩展:云的下一站真是雾计算?
  3. 分析文件上传过程中的HTTP头部
  4. golang 数据库null值错误 解决方法
  5. 数据解析1:XML解析(2)
  6. android listview设置选中时的item的背景色
  7. html填满剩余空间,html – 标题,两侧填充剩余空间
  8. 计算机地图制图的点状符号制作,地理空间信息符号化表达研究
  9. 【蓝桥杯每日一练】 汉诺塔
  10. java输出值_java参数传递(超经典)(强烈建议自己先写出程序的输出值)
  11. extjs6入门:用sencha cmd搭建简单的extjs6项目
  12. AcWing 902. 最短编辑距离(线性DP)
  13. 有关古文的C语言编程题,文言文考试也编程,文言语言!!!(附c/c++自译)
  14. Java进行音视频转码
  15. 等级保护体系、信息安全管理体系及等级保护管理制度
  16. SEO和SEM是什么?又有什么区别?
  17. 电子设计大赛-无线电类题目分析
  18. 视觉SLAM十四讲——第四讲李群与李代数
  19. TA100 T3.4
  20. 阿里云服务器部署项目邮箱发送功能465端口报错

热门文章

  1. 高分辨率对地观测系统重大科技专项简介
  2. linux关于日志文件介绍,Linux下重要日志文件介绍
  3. CSS技巧系列--使用视频作为背景
  4. chai断言库学习3-Core Plugin Concepts
  5. 第八次网页前端培训(JavaScript)
  6. windows 配置永久路由
  7. paper 43 :ENDNOTE下载及使用方法简介
  8. knn sklearn
  9. 云服务器密码登录异常的解决办法
  10. soot基础 -- 相关数据结构SootClass,SootMethod,SootBody,Unit的进一步说明