在 iOS 9.0 之后,UIView 中新增了一个 UIViewLayoutConstraintCreation
分类,该分类声明了一些 NSLayoutAnchor
属性,用来简化视图约束的创建。
NSLayoutAnchor 虽然给出了 NSLayoutXAxisAnchor
、NSLayoutYAxisAnchor
、NSLayoutDimension
三个子类,分别用来描述横向、纵向和长宽的相关信息,但是并没有声明任何属性或初始化方法。
从 UIView 的 UIViewLayoutConstraintCreation 分类中属性的只读特性来看,其是由框架内部创建,并且完全是对开发者透明的,使用的形式如下:
[myView.topAnchor constraintEqualToAnchor:otherView.topAnchor constant:10];
那么,NSLayoutAnchor 更像是对 NSLayoutAttribute 的封装。如果根据约束的核心公式推断,该类中也定然保存着相关联的视图。
UILayoutGuide 为了适配不同的机型,规避状态栏或导航栏带来的视图布局影响,可以使用 UIViewController 分类 UILayoutSupport 中的 topLayoutGuide
和 bottomLayoutGuide
属性。不过 iOS 11.0 之后,使用 UIView 中的 safeAreaLayoutGuide
属性更加方便。 这个属性是 UILayoutGuide
类,该类在 iOS 9.0 就生效了。巧的是,其也声明了同 UIViewLayoutConstraintCreation
分类中相同的属性,且是只读的。
1 2 3 @property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *leadingAnchor; //... @property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *centerYAnchor;
实际上,从 safeAreaLayoutGuide 属性就可以知道,UILayoutGuide 实例对象是可以用来创建约束的,其定义了布局中的一个长方形区域,同视图类似,但是他们并不会显示在视图层级中,也不会响应相关的事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - (void)btnClick { NSMutableArray *btns = [NSMutableArray arrayWithCapacity:2]; for (int i=0;i<2;i++) { UIButton *btn = [[UIButton alloc]init]; btn.backgroundColor = UIColor.redColor; btn.translatesAutoresizingMaskIntoConstraints = NO; [btns addObject:btn]; [self.view addSubview:btn]; } NSMutableArray *guides = [NSMutableArray arrayWithCapacity:3]; for (int i=0;i<3;i++) { int a = 1; if (a) { UILayoutGuide *guide = [[UILayoutGuide alloc]init]; [self.view addLayoutGuide:guide]; [guides addObject:guide]; }else { UIView *guide = [[UIView alloc]init]; guide.backgroundColor = UIColor.greenColor; guide.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:guide]; [guides addObject:guide]; } } UILayoutGuide *guide1 = guides[0]; UILayoutGuide *guide2 = guides[1]; UILayoutGuide *guide3 = guides[2]; UIButton *btn1 = btns[0]; UIButton *btn2 = btns[1]; [guide1.leftAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leftAnchor constant:15].active = YES; [guide1.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES; [btn1.leftAnchor constraintEqualToAnchor:guide1.rightAnchor].active = YES; [btn1.topAnchor constraintEqualToAnchor:guide1.topAnchor].active = YES; [guide2.leftAnchor constraintEqualToAnchor:btn1.rightAnchor].active = YES; [guide2.topAnchor constraintEqualToAnchor:guide1.topAnchor].active = YES; [btn2.leftAnchor constraintEqualToAnchor:guide2.rightAnchor].active = YES; [btn2.topAnchor constraintEqualToAnchor:guide1.topAnchor].active = YES; [btn2.widthAnchor constraintEqualToConstant:100].active = YES; [btn2.widthAnchor constraintEqualToAnchor:btn1.widthAnchor].active = YES; [btn2.heightAnchor constraintEqualToConstant:50].active = YES; [btn2.heightAnchor constraintEqualToAnchor:btn1.heightAnchor].active = YES; [guide3.leftAnchor constraintEqualToAnchor:btn2.rightAnchor].active = YES; [guide3.topAnchor constraintEqualToAnchor:guide1.topAnchor].active = YES; [guide3.rightAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.rightAnchor constant:-15].active = YES; [guide3.widthAnchor constraintEqualToAnchor:guide2.widthAnchor].active = YES; [guide3.widthAnchor constraintEqualToAnchor:guide1.widthAnchor].active = YES; [guide3.heightAnchor constraintEqualToAnchor:guide2.heightAnchor].active = YES; [guide3.heightAnchor constraintEqualToAnchor:guide1.heightAnchor].active = YES; [guide3.heightAnchor constraintEqualToConstant:50].active = YES; }
这个例子表明在布局时,UILayoutGuide
的使用可以帮助我们减少不必要的视图。但是也可以看出, 虽然 NSLayoutAnchor
类和 NSLayoutConstraint
类属性 active
的使用使得约束的创建简化了很多,但是可读性仍然很差,如果出现错误,查找十分麻烦。
而 Masonry
框架是不支持 UILayoutGuide
的,不过,其声明了的 MASAdditions
分类中提供了针对这种情况的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef NS_ENUM(NSUInteger, MASAxisType) { MASAxisTypeHorizontal, MASAxisTypeVertical }; - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
这两个方法可以在水平或垂直方向上排列数组中的视图,区别是视图之间的间隔是固定的值或者视图的宽高是固定值。