文章转自:http://tutuge.me/2015/05/23/autolayout-example-with-masonry/?comefrom=http://blogread.cn/news/
好久没有写Blog了,这段时间有点忙啊=。=
本文举了3个比较有“特点”的Autolayout例子,源于微博上好友的提问,感觉比较有意思,也比较有代表性,就写了出来,分享给大家~
至于为什么用Masonry,那是因为它好用啊!(被问到过有关Masonry的问题,就索性用它来实现吧=。=)。
https://github.com/zekunyan/AutolayoutExampleWithMasonry
实现的时候采用的是用StoryBoard拖拽约束+Masonry手写代码相结合的方式实现。最关键的地方是用Masonry,为了更好地突出重点。其它的无关紧要的空间约束,直接就拖拽了。
刚开始学习Autolayout的时候,什么“Leading Edges”、“Horizontal Centers”,好多啊,感觉一下子适应不来,有时候面对一个界面布局上的需求,可能都无从下手。
总的来说,我觉得Autolayout的关键就是“Constraint(约束)”。其实就是一下两点:
既然没有了具体设置View的frame属性,也就是说,系统会在运行时,通过我们设定的“约束”,计算出每个View的frame,再去绘制屏幕内容。
也就是说,我们设置的Constraint,要能体现出View的位置(x、y坐标)、大小(宽高)。无论是用IB拖拽约束,还是手写代码,只要从这个角度去思考,很多问题就都能解决。
有关Autolayout的知识,网上有很多,在这里就不详细列出了,但是有个公式倒是可以贴出来:
1
|
viewA-attribute = viewB-attribute * multiplier + constant
|
好用!
遇到这种跟内容压缩、优先级有关的布局,就不得不提Autolayout中的两个重要的属性“Content Compression Resistance”和“Content Hugging”。
对,这个属性说白了就是“不许挤我”=。=
这个属性的优先级(Priority)越高,越不“容易”被压缩。也就是说,当整体的空间装不小所有的View的时候,Content Compression Resistance优先级越高的,现实的内容越完整。
这个属性的优先级越高,整个View就要越“抱紧”View里面的内容。也就是View的大小不会随着父级View的扩大而扩大。
根据要求,可以将约束分为两个部分:
重点:
关键的代码如下:(label1是左边的label,label2是右边的)
// label1: 位于左上角
[_label1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(_contentView1.mas_top).with.offset(5);
make.left.equalTo(_contentView1.mas_left).with.offset(2);
// 40高度
make.height.equalTo(@40);
}];
// label2: 位于右上角
[_label2 mas_makeConstraints:^(MASConstraintMaker *make) {
//左边贴着label1
make.left.equalTo(_label1.mas_right).with.offset(2);
//上边贴着父view
make.top.equalTo(_contentView1.mas_top).with.offset(5);
//右边的间距保持大于等于padding.right,注意是lessThanOrEqual
make.right.lessThanOrEqualTo(_contentView1.mas_right).with.offset(-2);
//只设置高度40
make.height.equalTo(@40);
}];
//设置label1的content hugging 为1000
[_label1 setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
//设置label1的content compression 为1000
[_label1 setContentCompressionResistancePriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
//设置右边的label2的content hugging 为1000
[_label2 setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
//设置右边的label2的content compression 为250
[_label2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisHorizontal];
灵活运用“Content Compression Resistance”和“Content Hugging”属性。
先看看示例的截图:
下面的四个Switch控件分别控制上面对应位置的图片是否显示。
解释
之所以这么设置,主要目的有以下几点:
先看看设置每个imageView约束的函数:
/**
* 设置view的宽高、左边约束,垂直中心约束
*
* @param view 要设置的view
* @param size CGSize
* @param left 左边对齐的约束
* @param centerY 垂直中心对齐的约束
*
* @return 返回宽约束,用于显示、隐藏单个view
*/
- (MASConstraint *)setView:(UIView *)view size:(CGSize)size left:(MASViewAttribute *)left centerY:(MASViewAttribute *)centerY {
__block MASConstraint *widthConstraint;
[view mas_makeConstraints:^(MASConstraintMaker *make) {
//宽高固定
widthConstraint = make.width.equalTo(@(size.width));
make.height.equalTo(@(size.height));
//左边约束
make.left.equalTo(left);
//垂直中心对齐
make.centerY.equalTo(centerY);
}];
return widthConstraint;
}
接着就是设置容器View的代码:
//containerView 就是 容器View
[_containerView mas_makeConstraints:^(MASConstraintMaker *make) {
//只设置高度,宽度由子View决定
make.height.equalTo(@(IMAGE_SIZE));
//水平居中
make.centerX.equalTo(self.view.mas_centerX);
//距离父View顶部200点
make.top.equalTo(self.view.mas_top).offset(200);
}];
最后是内部imageView的约束:
//分别设置每个imageView的宽高、左边、垂直中心约束,注意约束的对象
//每个View的左边约束和左边的View的右边相等=。=,有点绕口...
UIImageView *imageView1 = _imageViews[0];
MASConstraint *width = [self setView:imageView1 size:imageViewSize left:_containerView.mas_left centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
UIImageView *imageView2 = _imageViews[1];
width = [self setView:imageView2 size:imageViewSize left:imageView1.mas_right centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
UIImageView *imageView3 = _imageViews[2];
width = [self setView:imageView3 size:imageViewSize left:imageView2.mas_right centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
UIImageView *imageView4 = _imageViews[3];
width = [self setView:imageView4 size:imageViewSize left:imageView3.mas_right centerY:_containerView.mas_centerY];
[_widthConstraints addObject:width];
//最后设置最右边的imageView的右边与父view的最有对齐
[imageView4 mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(_containerView.mas_right);
}];
控制ImageView显示、隐藏的时候,直接让其宽度等于0就行:
- (IBAction)showOrHideImage:(UISwitch *)sender {
NSUInteger index = (NSUInteger) sender.tag;
MASConstraint *width = _widthConstraints[index];
if (sender.on) {
width.equalTo(@(IMAGE_SIZE));
} else {
width.equalTo(@0);
}
}
有时候用个“容器View”管理内部的View,往往会起到事半功倍的效果。而且在组织约束的时候,尽量的将约束统一起来,这样可以用一个函数去设置,减少代码量。
其实这个很简单=。= 再看看这个公式:
1
|
viewA-attribute = viewB-attribute * multiplier + constant
|
这个是Autolayout里面一个约束的不同属性的基本组合关系,替换成宽度的话,就是下面这样:
1
|
子View的宽度 = 父级View宽度 * 系数 + 常数;
|
在Masonry里面,其实有个函数“multipliedBy”,就是用来设置multipler属性的(跟原本的NSLayoutConstraint的对应)。
如下:
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
//上下左贴边
make.left.equalTo(_containerView.mas_left);
make.top.equalTo(_containerView.mas_top);
make.bottom.equalTo(_containerView.mas_bottom);
//宽度为父view的宽度的一半
make.width.equalTo(_containerView.mas_width).multipliedBy(0.5);
}];
接着,只要控制父级View的宽度,子View的宽度就会随着变化了。
multipliedBy在Masonry的Github主页里面没有=。=
所以要养成读头文件的习惯~
有关Autolayout的东西还有好多没有写,什么动画啊、动态修改约束之类的,本文也算是个引子吧,任重而道远~
能看到这的朋友,也算是很有耐心了,哈哈~~