使用场景

给视图对象快速创建约束,可以使用比较冷门的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

参考链接

Comments