UI布局是app开发的很重要的一个环节,经历了从坐标布局到相对布局的过程,苹果推出的NSLayoutConstraint布局的方式可以通过指定view之间的相对位置来实现布局,我们先来看看使用NSLayoutConstraint布局的方式的实现,然后深度剖析下如何设计一款自动布局的框架。
NSLayoutConstraint
好吧 咱们一言不合就开始上代码吧,哈哈!!!
1 NSLayoutConstraint *constaintTop = [NSLayoutConstraint 2 constraintWithItem:self.tableView 3 attribute:NSLayoutAttributeTop 4 relatedBy:NSLayoutRelationEqual 5 toItem:self.view
attribute:NSLayoutAttributeTop 6 multiplier:1.0 7 constant:20]; 8 9 [_tableView addConstraint:constaintTop];
首先我们来看NSLayoutConstraint,顾名思义,是布局约束的意思,我们进入看看它的类结构:
1.这一段主要定义了它的一些属性的枚举,分别对应了Layout的一些属性,Left,Right,Top,Bottom,Center等等,我们仔细看看上面代码的参数:
constraintWithItem:id类型的,也就是说不限于UIView,如果是其它类型的会出现神马情况呢....
attribute:NSLayoutAttribute枚举类型的,也就是数字了。
relatedBy:NSLayoutRelation枚举类型的,也就是数字了。
toItem:id类型的,也就是说不限于UIView,如果是其它类型的会出现神马情况呢....
1 typedef NS_ENUM(NSInteger, NSLayoutRelation) { 2 NSLayoutRelationLessThanOrEqual = -1, 3 NSLayoutRelationEqual = 0, 4 NSLayoutRelationGreaterThanOrEqual = 1, 5 }; 6 7 typedef NS_ENUM(NSInteger, NSLayoutAttribute) { 8 NSLayoutAttributeLeft = 1, 9 NSLayoutAttributeRight, 10 NSLayoutAttributeTop, 11 NSLayoutAttributeBottom, 12 NSLayoutAttributeLeading, 13 NSLayoutAttributeTrailing, 14 NSLayoutAttributeWidth, 15 NSLayoutAttributeHeight, 16 NSLayoutAttributeCenterX, 17 NSLayoutAttributeCenterY, 18 NSLayoutAttributeBaseline, 19 NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline, 20 NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0), 21 22 23 NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0), 24 NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0), 25 NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0), 26 NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0), 27 NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0), 28 NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0), 29 NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), 30 NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), 31 32 NSLayoutAttributeNotAnAttribute = 0 33 }; 34 35 typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) { 36 NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft), 37 NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight), 38 NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop), 39 NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom), 40 NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading), 41 NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing), 42 NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX), 43 NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY), 44 NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline), 45 NSLayoutFormatAlignAllLastBaseline = NSLayoutFormatAlignAllBaseline, 46 NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline), 47 48 NSLayoutFormatAlignmentMask = 0xFFFF, 49 50 /* choose only one of these three 51 */ 52 NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default 53 NSLayoutFormatDirectionLeftToRight = 1 << 16, 54 NSLayoutFormatDirectionRightToLeft = 2 << 16, 55 56 NSLayoutFormatDirectionMask = 0x3 << 16, 57 };
2.我们注意到里面有两个初始化的方法,用来初始化当前的constraint属性,对应的参数分别为 view1,attr1,relation,view2,attr2,multiplier,constant刚好是对应两个view的属性之间的关系,通过倍数以及修正值来准确描述相对的位置关系,view1.att1=view2.attr2*multiplier+constant;
/* Create constraints explicitly. Constraints are of the form "view1.attr1 = view2.attr2 * multiplier + constant" If your equation does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute. */ +(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c; /* Create an array of constraints using an ASCII art-like visual format string. */ + (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;
3.另外我们发现有相对应的一些属性,注意到它们都是只读属性
1 @property (readonly, assign) id firstItem; 2 @property (readonly) NSLayoutAttribute firstAttribute; 3 @property (readonly) NSLayoutRelation relation; 4 @property (nullable, readonly, assign) id secondItem; 5 @property (readonly) NSLayoutAttribute secondAttribute; 6 @property (readonly) CGFloat multiplier;
从这个类的基本结构来看,橘子君对NSLayoutConstraint的理解就是:一个定义了两个view的相对关系以及能准确的描述出属性的相对数值的一个类。
好吧,橘子君的理解是,上述的一串代码是可以准备的描述出一个约束的,感觉代码结构还是挺清晰的,但是貌似有那么一点点的冗长。
我们再回过头来看看上面的一段代码吧
1 NSLayoutConstraint *constaintTop = [NSLayoutConstraint 2 constraintWithItem:self.tableView 3 attribute:NSLayoutAttributeTop 4 relatedBy:NSLayoutRelationEqual 5 toItem:self.view attribute:NSLayoutAttributeTop 6 multiplier:1.0 7 constant:20]; 8 9 [_tableView addConstraint:constaintTop];
上面的代码是通过constraintWithItem这个初始化方法来实现一个约束的,后面通过addConstraint方法添加到view上面去的,那么addConstraint方法里面主要做了哪些操作了,好吧,本着一探到低的精神,我们看看先
// Installing Constraints /* A constraint is typically installed on the closest common ancestor of the views involved in the constraint. It is required that a constraint be installed on _a_ common ancestor of every view involved. The numbers in a constraint are interpreted in the coordinate system of the view it is installed on. A view is considered to be an ancestor of itself. */ @interface UIView (UIConstraintBasedLayoutInstallingConstraints) @property(nonatomic,readonly) NSArray<__kindof NSLayoutConstraint *> *constraints NS_AVAILABLE_IOS(6_0); - (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0); // This method will be deprecated in a future release and should be avoided. Instead, set NSLayoutConstraint's active property to YES. - (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints NS_AVAILABLE_IOS(6_0); // This method will be deprecated in a future release and should be avoided. Instead use +[NSLayoutConstraint activateConstraints:]. - (void)removeConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0); // This method will be deprecated in a future release and should be avoided. Instead set NSLayoutConstraint's active property to NO. - (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints NS_AVAILABLE_IOS(6_0); // This method will be deprecated in a future release and should be avoided. Instead use +[NSLayoutConstraint deactivateConstraints:]. @end
好吧,原来是写在了UIView的分类里面,UIConstraintBasedLayoutInstallingConstraints,按字面意思理解,基本布局初始化约束,它将这些约束添加到数组里面,然后计算出它们的位置。
各位看官应该累了吧,上张美女图片大家养养眼,哈哈!!!
至此,苹果原生的NSLayoutConstraint分析完毕,那么问题来了,很多吃瓜观众觉得太反锁了,如是乎,各位童鞋们进行了包装美化,市场上便出现了各式各样的开源框架,这就像当明星一样,只要那些运气好,颜值高的才能真正的站在无赖上上,像Masonry,SDAutoLayout,ZXPAutoLayout,Keep Layout,Bee Framework。
好吧,如果需要我们自己设计一套布局的接口,该有哪些思路呢???
我们先上一些美女图片吧!!!