昨天从<<iOS Auto Layout开发秘籍>>的书中看到了我们在实际开啊中不常用看到的VFL和系统的自动布局类.虽然我们的前辈们已经开源了masonry等自动布局框架,但是他们还是在系统的框架基础上进行的再次封装.因为系统的自动布局约束看起来并不是那么简约.代码量比较多.但是我们在使用第三方约束的时候就应该明白,我们系统约束到底是什么样的.今天就给大家带来系统的自动布局约束类.在此并不包括IB中的自动布局.
系统原生自动布局
1.系统原生的自动布局还有划分,一种就是masonry封装的
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullableid)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c
如果你看过masonry的源码肯定会看到这个方法.因为他最终都是通过这个方法对需要约束的视图进行约束的.
还有一种就是我们今天分享的重点VFL.以前都只是听说过系统有这种布局方式但是从来没有用过.那么系统提供两种布局方式到底有什么异同啦.
第一种约束方式.
先来敲一段和masonry使用同样方式的约束吧.
首先我们定义一个视图让其距离left 64, right 64,top 64,bottom 64.
由于开发都用masonry所以系统的方法很多都是记忆很模糊.那么我们首先来看看系统的这些方法.
前两个是添加约束的方法,后面两个书删除约束的方法.
这两个方法很简单,一个参数是NSLayoutConstraint,一个是数组,但数组元素还是NSLayoutConstraint.这个就不多解释了.如果有人不知道NSLayoutConstraint,那么就说一下吧.
从字面意思就能看出来他到底是干什么的了.NS不多解释苹果老大哥的Foundation的标识例如NSArray.layout就是布局的意思.那后面这个是什么啦?然而这个就是我们的约束.
上面的方法是在UIView的分类中添加的,那结果就是只要你继承或间接继承都会有这个添加和删除的方法.
从上面这张截图中我们看到了每个方法中都有update这个关键字.那么我们的初步估计就是更新约束.或者是更新约束后更新视图.苹果的方法最大的特点是意思写在明面上,这也就要求我们在定义我们的方法时一定要能从方法名看出作用.
1.updateConstraintsIfNeeded调用这个方法会立即更新约束.
2.系统实际用来更新约束的方法
3.这个方法的调用的返回值决定是否去掉用系统实际更新约束的方法.作为正常布局的一部分.
4.告诉layoutView需要更新约束,在下次计算或者更新约束会更新约束
从上面的方法我们可以总结出以下几点.当我们在未来的某个时刻需要更新约束时,会首先调用setNeedsUpdateConstraints.这个方法.然后在某个时刻上调用updateConstraintsIfNeeded这个方法.这个方法调用会调用updateConstraints这个方法来完成正常的更新约束.
如果只是淡淡的调用updateConstraintsIfNeeded这个方法,可能并不会调用updateConstraints这个方法来更新约束.因为吐过视图上的约束可能还没有发生变化且没有标记需要更新.
系统在调用layoutSubviews时,就会调用updateContraintsIfNeeded,通过更新约束,用super view到subview的层次顺序,来计算frame,返向确定布局.
stackoverFlow上有关于改变约束的方法研究.
1.如果想立即改变约束,调用setNeedsLayout
2.吐过想改变view的一些属性如offsets可能会导致布局的改变,那么会调用setNeedsUpdateConstraints,更多的时候和面还需要加setNeedsLayout.
3.如果想立即改变布局,如会形成新的frame,那么需要在调用layoutIfNeeded.
系统中还有很多方法,就不一一介绍了.等到用到时在进行解释.
上面的布局只是一个简单的相对于self.view 的布局.那么两个视图应该怎样布局啦.
布局布标,view1和view2并排放置,view2的left距离view的right 15px.并且view1和view2等高等宽,并且centerY等于self.view的centerY.
好了如果左右间距都可以了,上下间距也是可以的.这个留在后面给大家当练习吧.
在这本书上还知道我们如何修改有歧义的约束和寻找因为不正确约束导致视图丢失的方法.
1.规则不一致的约束.有这么一个经典的例子就是viewA是viewB宽度的三倍,viewB是viewA宽度的2倍.请用代码实现.
当然代码我相信大家都能写的出来.但是肯定会有人问这个约束规则能实现吗?
答案是肯定的:绝对能实现.也毫无歧义,因为它的表述是完全正确的.在viewA的宽度=viewB的宽度*3和viewB的宽度=viewA的宽度*2这两个规则中并没有指定到底这两个宽度是多少,如果都是0那么这条规则就完全成立.所以我们最终就会看到视图的约束宽度为0.
2.缺失约束也可能导致丢失视图
举个简单的例子,就以我上面的代码为例:初始化frame为0,0,30,30.那么我给的约束是高度等于80,距离父视图left 0,top也是0,那么这个视图会在self.view上出现吗.并不会!并且约束也不会报错.那么视图为什么不出现啦.还记着我们没意识图需要自己添加约束时,讲一个属性置为了NO.translatesAutoresizingMaskIntoConstraints.什么意思啦.当我们设置这个属性时,默认的初始值(0,0,30,30)已经被废弃了.因为宽度没有确定,系统不能确定视图的宽度到底是多少,因而导致宽度为0,导致不可见.
还有就是介绍一下,添加约束的方法吧.
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullableid)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c
先来介绍几个参数吧
item:需要添加约束的视图
attr1:需要添加约束的属性,例如left,bottom,top.right,centerx,centerY.等.
relation:分为三种1.NSLayoutRelationLessThanOrEqual小于等于.NSLayoutRelationEqual等于.NSLayoutRelationGreaterThanOrEqual大于等于.
view2:item布局的参考视图
attr2:view2的left,right等属性.比如item的left相对于view2的right距离15.
multiplier:比例
constant:固定值,比如设定item的宽高时,如果不与其他视图参考.view2可以为nil.attr2可以为NSLayoutAttributeNotAnAttribute.
总结:view.attr1 = view.attr2*multiplier+constant.
第一种约束方式就讲到这.
VFL
可能很多人对VFL只知其名,但在现实中没用过.
就在今天让我们一起研究它,使用它.
可能对于上面的代码,我们的感觉是代码量好大啊,布局一个视图就这么多代码.那么当时图较多时,怎么办.苹果的工程师也想到了这个问题就出现了我们第二种布局方式:
Visual format language.
它和第一种的约束布局的区别主要在于代码量减少了.而且方法更加简单.先来看一段🌰吧.
举例self.view上下左右各64.
然后我们来分析代码:
+ (NSArray<__kindofNSLayoutConstraint*> *)constraintsWithVisualFormat:(NSString*)format options:(NSLayoutFormatOptions)opts metrics:(nullableNSDictionary *)metrics views:(NSDictionary *)views;
我们看到这个方法的返回值是一个数组.数组中是这个NSLayoutConstraint对象.
参数format:我们从上面的代码就可以看出约束都是写在format中的.an ASCII art-like visual format string
opts:文档中的大概意思是,属性的描述和布局的仿效都是对象并且在visual format 字符串中.
metrics:这个字典的键必须是visual format字符串 ,他的值必须是NSnumber对象.在visual format字符串中的常量都在这个字段中.
views:visula format字符串中出现的views,保存在这个子弹中,键也是visual format字符串.值是view object.
好了看完这个我们应该仔细看看visual format 字符串.
H:|-64-[view]-64-| 这个字符串的意思是:水平方向距离左边64,右边64.
V:|-64-[view]-64-| 这个字符串的意思是:垂直方向举例上边64,下边64.
那么高度和宽度怎么设置啦.
很简单:
V:|-64-[view(==80)] 这个就是距离top 64 高度等于80.设置宽度类似 只需要将V改为H就表示为,水平方向距离左边64,宽度为80.
VFL最大的缺点是什么啦.他不能表示相对比例和相对居中的约束.
那么两个视图的布局该怎么设置啦.
改有很多关于VFL的用法,这里就不多介绍了,可以查看苹果官方文档和(http://study1234.com/autolayoutshen-ru-qian-chu-liu-chun-dai-ma-de-pian-zhi/amp/)
在介绍几个方法就结束今天的分享了:
//调用这个方法来判断视图的约束是否充分/是否有歧义.
hasAmbiguousLayout
//可以获得不同方向上的约束
- (NSArray<__kindofNSLayoutConstraint*> *)constraintsAffectingLayoutForAxis:(UILayoutConstraintAxis)axisNS_AVAILABLE_IOS(10_0);
//这个方法会随机改变视图的layout到另外一个有效的layout。这样我们就可以很清楚的看到哪一个layout导致了整体的布局约束出现了错误,或者我们应该增加更多的布局约束。
exerciseAmbiguityInLayout
//让视图知道下次布局时需要重新计算.
//[view invalidateIntrinsicContentSize];
//压缩阻力Size Inspector指定
//代码设置一个视图的压缩阻力.水平轴和垂直轴都需要分别设置.设置的值可以再1到1000之间,默认值为750;
//[view setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal];
////代码设置一个试图的内容吸附优先级.默认值为250;
//[view setContentHuggingPriority:501 forAxis:UILayoutConstraintAxisHorizontal];
我在这里先解释一下压缩阻力和内容吸附.
压缩阻力.就是指保护其内容的方式,压缩阻力高的视图可以抵抗压缩,不允许内容被裁减.低的压缩阻力内容会被裁剪.虽然我们空间本身存在着压缩阻力.但是如果他的优先级较低.如果我们改变约束,将其宽度较小.它的内容就会被裁剪.要想不被裁剪我们可以手动修改他的重要性.
//[view setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal];
内容吸附:视图的大小与内容大小匹配的原则.
较高的优先级不允许视图将其伸展.防止出现大片留白.较低的优先级允许被拉伸,出现大量留白.
[view setContentHuggingPriority:501 forAxis:UILayoutConstraintAxisHorizontal];
我们可以手动改变其优先级.确定是否允许被拉伸.
我们在设置约束时也可以设置约束的优先级.
1000为最高.50为最低.
好了我们不怎么熟悉的自动布局就先说到这里.后面还会有关于自动布局的分享.例如自动布局的动画.IB中的自动布局等.
如果喜欢小编,可以点击关注,我会不定期的更新一些文章,也可以关注我的专题
本人联系方式:qq:513961360
vx:扫描下方二维码:
希望能有志同道合的好友加我.聊技术.聊生活都可以.
email:513961360@qq.com
也可以加我们的qq群希望能与朋友们一起聊天和学习.群里还有很多iOS开发者,帮助我们解决问题,并且同时学习.
qq群号:580284575