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,SDAutoLayoutZXPAutoLayout,Keep Layout,Bee Framework。

好吧,如果需要我们自己设计一套布局的接口,该有哪些思路呢???