NSLayoutAnchor

在 iOS 9.0 之后,UIView 中新增了一个 UIViewLayoutConstraintCreation 分类,该分类声明了一些 NSLayoutAnchor 属性,用来简化视图约束的创建。

NSLayoutAnchor 虽然给出了 NSLayoutXAxisAnchorNSLayoutYAxisAnchorNSLayoutDimension 三个子类,分别用来描述横向、纵向和长宽的相关信息,但是并没有声明任何属性或初始化方法。

从 UIView 的 UIViewLayoutConstraintCreation 分类中属性的只读特性来看,其是由框架内部创建,并且完全是对开发者透明的,使用的形式如下:

[myView.topAnchor constraintEqualToAnchor:otherView.topAnchor constant:10];

那么,NSLayoutAnchor 更像是对 NSLayoutAttribute 的封装。如果根据约束的核心公式推断,该类中也定然保存着相关联的视图。

UILayoutGuide

为了适配不同的机型,规避状态栏或导航栏带来的视图布局影响,可以使用 UIViewController 分类 UILayoutSupport 中的 topLayoutGuidebottomLayoutGuide 属性。不过 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;

这两个方法可以在水平或垂直方向上排列数组中的视图,区别是视图之间的间隔是固定的值或者视图的宽高是固定值。

Older Post

Git 学习笔记

随着软件工程的发展,开发者之间的合作开发变得越来越重要,为了解决不同开发者之间的协同开发问题,集中化的版本控制系统(CVCS,Centralized Version Control System)应运而生。 CVCS 固然提高了软件开发的效率,但是其也有一些缺陷。如果网络发生异常,或者中央服务器宕机 …

继续阅读