0%

VFL 深入浅出

使用场景

给视图对象快速创建约束,可以使用比较冷门的 VFL(Visual Format Language),本质是是基于自动布局(AutoLayout)。

解决的问题

以一种直观的方式,为视图创建约束

怎样解决

核心方法:

1
2
3
4
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(nullable NSDictionary<NSString *,id> *)metrics
views:(NSDictionary<NSString *, id> *)views;

Example:

1
2
3
4
[NSLayoutConstraint constraintsWithVisualFormat:@"|-[button1]-[button2]-[textField(>=20)]-|"
options:0
metrics:metrics
views:views]

参数:

  • format: 指定约束的格式。更多信息,在 Auto Layout Guide 查看 Visual Format Language
  • opts: 描述在视觉格式化字符串中的布局属性和方向
  • metrics: 将出现在视觉格式化字符串的常量字典。字典的 Keys 必须是在出现在视觉格式化字符串的字符串类型,对应的 values 必须是 NSNumber 对象。
  • views: 出现的视觉格式化字符串的字典 view,所有的 Keys 必须是使用在视觉格式化字符串的字符串类型,对应的 values 必须是 view 对象。

返回值:

一个约束组合的数组,像视觉格式化字符串描述的一样表述了在提供的视图和它们的父视图之间的关系。所有的约束以在视觉格式化字符串被指定的约束顺序一致。

效率和可维护性

需要对 VFL 理解比较全面和深入,有一定的学习成本,维护起来比较困难,但是如果理解的比较深刻,对于简单的布局效率非常高,比直接创建约束要直观。

最佳实践

  • 每条约束格式字符串分水平( H ,可省略)和垂直方向( V )。
  • | 代表父视图。
  • - 代表标准间距,两个子视图直接的值是 8,与父视图之间的值是 16。
  • [view] 每个视图必须用[]包裹起来,否则语法错误。
  • [view(>=44)] 可以为每个视图设置一些属性或者关系,写一个紧跟 view 后的括号集合,支持宽高,优先级,和其它视图之间的关系。
  • [view@20] 可以设置视图的约束的优先级,以@开头,取值范围(0 1000]。
  • [view1]-20-[view2] 可以指定 view 之间的水平或者垂直间距,写在一对 - 即可。
  • [view1][view2] 如果 - 省略则他们之间的距离为 0。
  • [flexibleButton(>=70,<=100)] 多个条件之间用 , 连接并且之间不能有空格。

其它方案

  1. 使用 NSLayoutConstraint 的类方法创建(iOS96.0 及以上可用)
1
2
3
4
5
6
7
+ (instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(nullable id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c;
  1. 使用 NSLayoutAnchor 的工厂方法创建(iOS9.0 及以上可用)
1
2
3
4
5
6
7
- (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor;
- (NSLayoutConstraint *)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor;
- (NSLayoutConstraint *)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor;

- (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c;
- (NSLayoutConstraint *)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c;
- (NSLayoutConstraint *)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c;

使用注意事项

  • 使用时,必须把 view 的 translatesAutoresizingMaskIntoConstraints 属性设置为 NO ,否则约束可能更预期不一致。这是一个历史遗留的问题,由于在 AutoLayout 诞生以前一直使用 Autoresizing 来控制布局.

  • 重写 updateConstraints() 方法,在里面计算约束,然后调用 setNeedsUpdateConstraints() 触发更新约束。

  • 系统计算布局顺序:

    • updateConstraints()
    • layoutSubviews()
    • drawRect(_:)

系统计算布局顺序参考

LayoutCycle

具体使用示例,查看下面的 Demo,样品工程


Demo 地址:VFLDemo

参考链接