NSLayoutAnchor
在 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
分类中相同的属性,且是只读的。
@property(nonatomic,readonly,strong) NSLayoutXAxisAnchor *leadingAnchor;
//...
@property(nonatomic,readonly,strong) NSLayoutYAxisAnchor *centerYAnchor;
实际上,从 safeAreaLayoutGuide 属性就可以知道,UILayoutGuide 实例对象是可以用来创建约束的,其定义了布局中的一个长方形区域,同视图类似,但是他们并不会显示在视图层级中,也不会响应相关的事件。
- (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
分类中提供了针对这种情况的方法。
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;
这两个方法可以在水平或垂直方向上排列数组中的视图,区别是视图之间的间隔是固定的值或者视图的宽高是固定值。