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。
好吧,如果需要我们自己设计一套布局的接口,该有哪些思路呢???