当前位置: 首页 > 科技观察

AutoLayout

时间:2023-03-12 09:06:25 科技观察

的纯代码实现,历史上比较用心。进公司也有两三个月了。我用Objective-C纯代码(虽然有时候偷偷混进去一些Swift开源库)写公司APP。在写布局的时候,几乎是要么在初始化时使用initWithFrame,要么在初始化之后使用view.frame。这种方法虽然很直观,一眼就能看出视图的位置和大小,但是也有缺点,比如计算麻烦等等。概述使用Objective-C纯代码写AutoLayout,AutoLayout字面理解就是自动布局,听上去挺酷的。说白了就是适配:适配和兼容各种情况,包括不同版本操作系统的适配(系统适配)和不同屏幕尺寸的适配(屏幕适配)。在Storyboard中,AutoLayout有以下三种常用的面板:1.Align(对齐)2.Pin(相对)3.ResolveAutoLayoutIssues(约束处理)本文不讲解AutoLayout在Storyboard中的实现,因为它是违背初心,方得千古名。Talkischeap,showmethecode首先说一下用代码实现AutoLayout的步骤,别眨眼:使用NSLayoutConstraint类创建具体的约束对象;将约束对象添加到对应的视图中,有两种代码:-(void)addConstraint:(NSLayoutConstraint*)constraint;-(void)addConstraints:(NSArray*)constraints;可能有人问了,原来只走两步就够了,我刚脱了裤子,给我看这个?!话不多说,直接上代码给大家看!下面看看我们是如何使用frame来确定一个view的位置的:-(void)viewDidLoad{[superviewDidLoad];self.title=@"Thewaytousetheframe";UIView*purpleView=[[UIViewalloc]initWithFrame:CGRectMake(100,200,150,150)];purpleView.backgroundColor=[UIColorpurpleColor];[self.viewaddSubview:purpleView];}代码很简单,运行效果如下:下面看一下AutoLayout的实现:-(void)viewDidLoad{[superviewDidLoad];self.title=@"AutoLayout的使用方式";UIView*purpleView=[[UIViewalloc]init];purpleView.backgroundColor=[UIColorpurpleColor];//禁止将AutoresizingMask转换为ConstraintspurpleView.translatesAutoresizingMaskIntoConstraints=NO;[self.viewaddSubview:purpleView];//添加宽度约束NSLayoutConstraint*widthConstraint=[NSLayoutConstraintconstraintWithItem:purpleViewattribute:NSLayoutAttributeWidthrelatedBy:NSLayoutRelationEqualtoItem:nilattribute:NSLayoutAttributeNotAnAttributemultiplier:0.0constant:150];[purpleViewaddConstraint:widthConstraint];//添加height约束NSLayoutConstraint*heightConstraint=[NSLayoutConstraintconstraintWithItem:purpleViewattribute:NSLayoutAttributeHeightrelatedBy:NSLayoutRelationEqualtoItem:nilattribute:NSLayoutAttributeNotAnAttributemultiplier:0.0constant:150];[purpleViewaddConstraint:heightConstraint];//添加left约束NSLayoutConstraint*leftConstraint=[NSLayoutConstraintconstraintWithItem:purpleViewattribute:NSLayoutAttributeLeftrelatedBy:NSLayoutRelationEqualtoItem:self.viewattribute:NSLayoutAttributeLeftmultiplier:1.0constant:100];[self.viewaddConstraint:leftConstraint];//添加top约束NSLayoutConstraint*topConstraint=[NSLayoutConstraintconstraintWithItem:purpleViewattribute:NSLayoutAttributeToprelatedBy:NSLayoutRelationEqualtoItem:self.viewattribute:NSLayoutAttributeTopmultiplier:1.0常数:200];[self.viewaddConstraint:topConstraint];}看完这段代码,惊呆了!被这一大段代码惊呆了。很多童鞋看到这么简单的布局都要写这么多代码。吓跑了,只能说一句:先别走,等我慢慢解释~创建约束对象的常用方法(NSLayoutConstraint)一个NSLayoutConstraint对象代表一个约束。+(id)constraintWithItem:(id)view1attribute:(NSLayoutAttribute)attr1relatedBy:(NSLayoutRelation)relationtoItem:(id)view2attribute:(NSLayoutAttribute)attr2multiplier:(CGFloat)multiplierconstant:(CGFloat)c;一共有7个参数🌚,下面以leftConstraint为例介绍一下这7个参数?view1:被约束的控件(purpleView)?attr1:约束的类型(constant),也就是做什么样的约束,大家可以进去看看有没有Constant(这里NSLayoutAttributeLeft)?relation:与引用控件的关系(常量),包括等于、大于等于、小于等于(NSLayoutRelationEqual表示等于)?view2:被引用控件(self.view)?attr2:constrained类型(常量)是做什么样的约束。可以进去看看有什么常量(这里是NSLayoutAttributeLeft)(NSLayoutAttributeLeft)?multiplier:乘数,也就是多少次(1.0)?c:constant,done这个常量(100)会加在上面的约束之后,所以leftConstraint的意思是:被约束的控件purpleView的左间距等于1.0乘以引用控件self.view的左间距加上100。于是我们得到AutoLayout的核心计算公式:obj1.property1=(obj2.property2*multiplier)+constantvalue1。添加约束规则(addConstraint)创建后,需要添加到活动控件中才能生效。注意在添加约束时,目标控件需要遵循以下规则(这里控件简单地用视图表示):(1)对于同级的两个视图之间的约束关系,将它们添加到它们的父视图中。对于同一级别的两个视图将它们之间的约束关系添加到它们的父视图中(2)对于两个不同级别的视图之间的约束关系,添加到它们最近的共同父视图对于两个不同级别的视图之间的约束关系,addtotheirnearestcommonparentview(3)对于有层次关系的两个视图之间的约束关系,添加到更高级别的父视图中对于有层次关系的两个视图之间的约束关系,添加到父视图上与更高级别的视图(4)例如,如果长和宽仅应用于视图本身,则将其添加到视图本身,不需要图片🌚可以看出,widthConstraint和Constraint属于类型(4),leftConstraint和rightConstraint属于类型(3)。一、AutoLayout代码实现注意事项如果只是创建和添加约束,是无法正常工作的。必须做以下工作:(1)先禁用autoresizing功能,防止AutoresizingMask转化为Constraints。为了避免冲突,需要设置view的以下属性为NO:view.translatesAutoresizingMaskIntoConstraints=NO;(2)在添加约束之前,确保相关控件已经在其父控件上。使用上面的例子是[self.viewaddSubview:purpleView];必须放在添加left约束之前,否则程序会崩溃,因为要保证purpleView已经在self.view上了。推荐写成[self.viewaddSubview:purpleView];首先,然后着重写约束。(3)您不需要为视图设置框架。可以看到这么简单的界面需要这么多代码,用AutoLayout来实现。感觉不太方便吧?事实上,AutoLayout取决于应用程序内容。以上只是一个使用的demo。如果你的内容信息很多,需要同时显示的分类很多,而且大小是动态的,比如微博列表,QQ动态列表等,使用AutoLayout写这些复杂的界面可以给(jǐyǔ🌚)帮助很大。为了简化AutoLayout复杂的代码,Apple开发了VFL语言(Visualformatlanguage)。其实我没看到太多的简化,有比较大的局限性。这里就不介绍了。童鞋想了解就去谷歌吧。算了,给个官方链接:https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html如何优雅地编码AutoLayout?看了苹果自己的AutoLayout实现,感觉真的很恶心,那么如何优雅的写AutoLayout呢?-使用第三方框架Masonry。GitHub:https://github.com/SnapKit/Masonry看它的介绍,感觉挺牛逼的:HarnessthepowerofAutoLayoutNSLayoutConstraintswithansimplified,chainableandexpressivesyntax.支持iOS和OSX自动布局。阅读自述文件。md文件发现确实相当优雅。先看一下Masonry是如何实现AutoLayout的:#import"ViewController.h"#import"Masonry.h"//第三方或者自写的用引号,系统自己用双引号。@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];UIView*purpleView=[[UIViewalloc]init];purpleView.backgroundColor=[UIColorpurpleColor];[self.viewaddSubview:purpleView];[purpleViewmas_makeConstraints:^(MASConstraintMaker*make){//在此块中,使用make对象创建约束make.size.mas_equalTo(CGSizeMake(100,100));make.center.mas_equalTo(self.view);}];}运行效果:创建一个long宽度为100且以父视图为中心的View注:purpleView.translatesAutoresizingMaskIntoConstraints=NO;这里就不用写了,因为Masonry已经写好了。Masonry在开车,上车一步一步跟着,哈哈哈哈//长宽都是100,贴在父view右下角[purpleViewmas_makeConstraints:^(MASConstraintMaker*make){make.width.equalTo(@100);make.height.equalTo(@100);make.right.equalTo(self.view);make.bottom.equalTo(self.view);}];长宽均为100,贴在父视图右下角//长宽均为100,贴在父视图右下角,间距为16[purpleViewmas_makeConstraints:^(MASConstraintMaker*make){make.width.equalTo(@100);make.height.equalTo(@100);//也可以写make.right。equalTo(self.view.mas_right).offset(-16);//为了增强可读性,可以在.offset(-16);看你的习惯make.right.equalTo(self.view).offset(-16);//你也可以这样写make.right.equalTo(self.view.mas_bottom).offset(-16);make.bottom.equalTo(self.view).offset(-16);}];长宽都是100,贴在父view的右下角,间距是16看上面代码中封装的@100,其实也可以直接传值100,只是改成equalTomas_equalTo,这样它会自动为你换行。make.width.mas_equalTo(100);make.height.mas_equalTo(100);其实mas_equalTo是一个宏,可以进去看看定义。mas_equalTo此方法将包装参数。equalTo这个方法不会包装参数。mas_equalTo的功能比equalTo强。你可能会感到有点头晕。有时您使用mas_equalTo,有时您使用equalTo。其实你可以在pch文件中定义两个。一个宏就可以完美解决这个纠结的问题。注意要写在#import"Masonry.h"之前。//definethisconstantifyouwanttouseMasonrywithoutthe'mas_'prefix,这样`mas_width`等可以写成`width`#defineMAS_SHORTHAND//definethisconstantifyouwanttoenableauto-boxingfordefaultsyntax,这样`mas_equalTo`和LS`equalTo`就和#defineMAS_SHORTHAND_G没什么区别了更复杂的界面:-(void)viewDidLoad{[superviewDidLoad];UIView*purpleView=[[UIViewalloc]init];purpleView.backgroundColor=[UIColorpurpleColor];[self.viewaddSubview:purpleView];UIView*orangeView=[[UIViewalloc]init];orangeView.backgroundColor=[UIColororangeColor];[self.viewaddSubview:orangeView];CGFloatmargin=16;CGFloaheight=32;[purpleViewmas_makeConstraints:^(MASConstraintMaker*make){make.left.equalTo(self.view).offset(边距);make.bottom.equalTo(self.view).offset(-margin);make.right.equalTo(orangeView.left).offset(-margin);make.height.equalTo(height);make.width.equalTo(orangeView);}];[orangeViewmas_makeConstraints:^(MASConstraintMaker*make){make.bottom.equalTo(self.view).offset(-margin);make.right.equalTo(self.view).offset(-margin);make.height.equalTo(height);}];}等高等宽的两个视图平分屏幕宽度,有一个间隙。其实这个接口的实现方式有很多种。你可以尝试一下。例如,这样写:-(void)viewDidLoad{...[purpleViewmas_makeConstraints:^(MASConstraintMaker*make){make.left.equalTo(self.view).offset(margin);make.bottom.equalTo(self.视图).offset(-margin);make.right.equalTo(orangeView.left).offset(-margin);make.height.equalTo(高度);make.height.equalTo(orangeView);make.width.equalTo(orangeView);make.top.equalTo(orangeView);}];[orangeViewmas_makeConstraints:^(MASConstraintMaker*make){make.right.equalTo(self.view).offset(-margin);}];}总结其实,Masonry的文档有很详细,建议大家看看文档,我写这个主要是为这个界面的Tableview的上下阻尼效果做准备