「这是我参与11月更文挑战的1天,活动详情查看:2021最后一次更文挑战」。
像我们常用的Masonry和SnapKit 都是基于Apple 自身的Auto Layout就行封装的,不过好像Masonry没有使用到NSLayoutAnchor这个新类。
Apple Auto Layout Constraints是在 iOS 6 中引入的,它改变了我们对 UI 元素布局的看法。但是在当时它并最优雅或最漂亮的API,反而有点繁琐。这就是为什么会有很多优秀的自动布局库不断的涌现。但从那时起,Apple 围绕 Auto Layout 的 API 进行了改进并且优化了不少。
iOS 6时期的自动布局
当自动布局在 iOS 6 中首次出现时,可以通过三种方式在用户界面布局中创建约束:
1. Interface Builder,我们不会在本文中讨论它。
2. 视觉格式语言 (VFL)。这是一种使用类似 ASCII 艺术的视觉格式字符串创建约束的方法。它看起来像这样|-[find]-[findNext]-[findField(>=20)]-|。
下面就是是VFL约束写法的一个简单的示例。
UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor yellowColor];
view1.translatesAutoresizingMaskIntoConstraints = NO;
[superView addSubview:view1];
UIView *view2 = [[UIView alloc]init];
view2.backgroundColor = [UIColor yellowColor];
view2.translatesAutoresizingMaskIntoConstraints = NO;
[superView addSubview:view2];
UIView *view3 = [[UIView alloc]init];
view3.backgroundColor = [UIColor yellowColor];
view3.translatesAutoresizingMaskIntoConstraints = NO;
[superView addSubview:view3];
NSString *hVFL = @"H:|-space-[view1(==100)]-space1-[view2]-space-|";
NSString *hVFL1 = @"H:|-space-[view3]-space-|";
NSString *vVFL = @"V:|-space-[view1(==view3)]-space-[view3]-space-|";
NSString *vVFL1 = @"V:|-space-[view2(==view3)]-space-[view3]-space-|";
NSDictionary *metircs = @{@"space":@20,@"space1":@30};
NSDictionary *views = NSDictionaryOfVariableBindings(view1,view2,view3);
NSArray *hconstraint = [NSLayoutConstraint constraintsWithVisualFormat:hVFL options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views];
NSArray *hconstraint1 = [NSLayoutConstraint constraintsWithVisualFormat:hVFL1 options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views];
NSArray *vconstraint = [NSLayoutConstraint constraintsWithVisualFormat:vVFL options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views];
NSArray *vconstraint1 = [NSLayoutConstraint constraintsWithVisualFormat:vVFL1 options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views];
//添加约束
[superView addConstraints:hconstraint];
[superView addConstraints:hconstraint1];
[superView addConstraints:vconstraint];
[superView addConstraints:vconstraint1];
对于简单的布局,这可能看起来是创建布局的好方法,但对于复杂的布局,情况可能并非如此,但它缺乏对高度/宽度百分比和居中等许多布局属性的支持,而且对于我们来说,这样字符串的一个写法很容易出错,而且不好检验。
3.NSLayoutConstrint API
它的缺点也很明显太冗长。下面就是一个简单的示例。
let leadingConstraint = NSLayoutConstraint(
item: contentView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1,
constant: 20)
let bottomConstraint = NSLayoutConstraint(
item: contentView,
attribute: .bottom,
relatedBy: .equal,
toItem: view,
attribute: .bottom,
multiplier: 1,
constant: -20)
let topConstraint = NSLayoutConstraint(
item: contentView,
attribute: .top,
relatedBy: .equal,
toItem: view,
attribute: .top,
multiplier: 1,
constant: 20)
let trailingConstraint = NSLayoutConstraint(
item: contentView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1,
constant: -20)
view.addConstraints([
leadingConstraint,
trailingConstraint,
topConstraint,
bottomConstraint
])
iOS 8时期 的改进
在 iOS 8 中,Apple 向 NSLayoutConstraint 引入了活动状态的概念。只有在活动状态中约束影响布局的计算。您可以启用/禁用任何约束,而无需将其删除并将其重新添加到视图中。
Activate/Deactivate a collection of constraints
添加约束的旧方式仍然与活动状态兼容;传入的所有约束都addConstraints(_:)将标记为活动的。但是它再也不是推荐的做法。所有约束必须只涉及接收视图范围内的视图。具体来说,任何涉及的视图必须是接收视图本身,或者接收视图的子视图。对于有经验的开发人员来说,这可能不是问题。但可能会让新手混淆他们应该向哪个视图添加约束。如果您尝试向错误的超类()添加约束,您可能会收到此错误。
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Impossible to set up layout with view hierarchy unprepared for constraint.'
在 iOS 8 中,Apple 引入了一种新方法activate(_:)。该activate(_:)方法会自动将约束添加到正确的视图中。也有deactivate(_:)作为removeConstraint(_:)。
NSLayoutConstraint.activate([ leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
NSLayoutConstraint.deactivate([ leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
而且你还可以通过更改isActive属性来激活/停用单个约束。
let leadingConstraint = NSLayoutConstraint(
item: contentView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1,
constant: 20)
leadingConstraint.isActive = true
leadingConstraint.isActive = false
iOS 9 时期的改进 NSLayoutAnchor
Apple 终于在 iOS 9 中解决了约束创建语法冗长的问题。将一个新的NSLayoutAnchor. 将其视为视图中的特定点,您可以在创建约束时将其用作参考点。每个anchor只能与同轴的anchor形成约束。NSLayoutAnchor带有一组方法,包括您会在NSLayoutConstraint的操作中看到,例如等于 (=)、大于或等于 (>=)、小于或等于 (<=)。所有这些都带有各种参数,包括乘数和常数。即使有很多变化,您也可以通过键入 访问所有这些constraint。要探索所有这些变化,请查看Apple 文档
下面表格数据来及Apple 文档
| Property | Layout Anchor | Description |
|---|---|---|
| bottomAnchor | NSLayoutYAxisAnchor | A layout anchor representing the bottom edge of the view’s frame. |
| centerXAnchor | NSLayoutXAxisAnchor | A layout anchor representing the horizontal center of the view’s frame. |
| centerYAnchor | NSLayoutYAxisAnchor | A layout anchor representing the vertical center of the view’s frame. |
| firstBaselineAnchor | NSLayoutYAxisAnchor | A layout anchor representing the baseline for the topmost line of the view’s frame. |
| heightAnchor | NSLayoutDimension | A layout anchor representing the height of the view’s frame. |
| lastBaselineAnchor | NSLayoutYAxisAnchor | A layout anchor representing the baseline for the bottommost line of the view’s frame. |
| leadingAnchor | NSLayoutXAxisAnchor | A layout anchor representing the leading edge of the view’s frame. |
| leftAnchor | NSLayoutXAxisAnchor | A layout anchor representing the left edge of the view’s frame. |
| rightAnchor | NSLayoutXAxisAnchor | A layout anchor representing the right edge of the view’s frame. |
| topAnchor | NSLayoutYAxisAnchor | A layout anchor representing the top edge of the view’s frame. |
| trailingAnchor | NSLayoutXAxisAnchor | A layout anchor representing the trailing edge of the view’s frame. |
| widthAnchor | NSLayoutDimension | A layout anchor representing the width of the view’s frame. |
简单的示例:
let constraints = [
contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
contentView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20)
]
NSLayoutConstraint.activate(constraints)
关于storyboard的Auto Layout布局,大家可以谷歌一下,这里我推荐一篇文章,同时下一篇文章将根据Apple Auto Layout的特性写一个简单的布局组件。