AutoLayoutDSL 1.0.0

AutoLayoutDSL 1.0.0

测试已测试
语言语言 Objective C++Objective C++
许可协议 MIT
发布最后发布2014年12月

David Whetstone维护。



 
依赖
BlocksKit~> 2.0
libextobjc/EXTScope~> 0.4
 

  • 编写者
  • David Whetstone

一个简洁的C++ DSL,用于更简洁地定义布局约束。

约束表达式

定义布局约束的标准语法很冗长。例如,为了指定一个按钮相对于另一个按钮的x偏移量,并且它们之间有5点间隙,可以这样写:

self.view addConstraint:[NSLayoutConstraint constraintWithItem:_button2
                                                      attribute:NSLayoutAttributeLeft
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:_button1
                                                      attribute:NSLayoutAttributeRight
                                                     multiplier:1.0
                                                       constant:5.0]];

使用布局DSL,这将简单地写成

View(_button2).left == View(_button1).right + 5.0;

或者更自然地写成

View(_button1).right + 5.0 == View(_button2).left;

就是这样。当执行这个表达式时,将创建一个NSLayoutConstraint对象,并将其自动安装在与_button1_button2最近的共同祖先中。

添加一些糖

如果愿意将代码编译为Objective-C++,可以访问更加简洁的约束定义。只需#import "UIView+AutoLayoutDSLSugar.h",上述约束可以这样定义:

_button1.right + 5.0 == _button2.left;

现在,这已经相当完美了。

约束安装

如前所述,仅声明约束表达式就足以在引用视图的最近的共同祖先中安装该约束,但如果你希望自己安装约束,只需在addConstraint:调用中使用约束表达式代替实际的NSLayoutConstraint对象即可。将表达式传递给该方法将阻止自动安装约束。

[self.view addConstraint:View(_button1).left == View(_button2).right + 5.0];

基本上,约束表达式可以在需要NSLayoutConstraint对象的地方使用。例如,可以将多个约束安装如下:

[self.view addConstraints:@[View(_button1).right + 5.0 == View(_button2).left,
                            View(_button2).right + 5.0 == View(_button3).left]];

但是,为什么这么麻烦,这样不是更简洁吗?

View(_button1).right + 5.0 == View(_button2).left;
View(_button2).right + 5.0 == View(_button3).left;

引用父视图的约束

要指定相对于父视图的视图的约束,只需从View规范中省略UIView*参数即可

View().left + 5.0 == View(_button1).left;

优先级

可以使用以^运算符为操作符的约束表达式来指定优先级

View(_button1).left == View(_button2).right + 5.0 ^ 999.0;

约束识别

可以在必要时通过添加标识符来简化单个约束或一组约束的删除和替换。有几种方法可以完成此操作。

在单个约束中使用标识符

就像添加优先级一样,可以通过使用 ^ 运算符将标识符添加到约束表达式中,如下所示

View(_image).width == View(_image).height * aspectRatio ^ @"maintainAspect";

将标识符添加到一组约束中

通过为它们分配相同的标识符来分组约束

View(_avatar).left == View().left + 5.0 ^ @"A group of constraints";
View(_label).left == View(_avatar).right + StandardHorizontalGap ^ @"A group of constraints";

但更干净的方法是使用约束分组宏

BeginConstraintGroup(@"A group of constraints")

View(_avatar).left == View().left + 5.0;
View(_label).left == View(_avatar).right + StandardHorizontalGap;

EndConstraintGroup;

查找约束

要查找具有特定标识符的单个约束

NSLayoutConstraint *constraint = [self.view constraintWithID:@"identifier"];

要查找具有特定标识符的所有约束

NSArray *constraint = [self.view constraintsWithID:@"identifier"];

要查找引用特定视图的所有约束

NSArray *constraints = [self.view constraintsReferencingView:_button1];

删除约束

要删除约束,只需调用 remove

NSLayoutConstraint *constraint = [self.view constraintWithID:@"identifier"];
[constraint remove];

注意事项

Objective-C++ (C++11)

此DSL要求将源文件编译为Objective-C++(只需更改其扩展名为 .mm)。这可能会引起一些人的担忧。如果您不想处理这些可能引起的问题,您仍然可以通过使用类别在单独的文件中定义所有约束来使用DSL。

自动与手动约束安装

我使用了一些非常规的方法来实现我想要的结果。通过从内部 ConstraintBuilder 对象的析构函数中安装新构建的 NSLayoutConstraint 对象来实现约束自动安装的功能。通过在内部 ConstraintBuilder 类上重载转换运算符来实现使用约束表达式替换 NSLayoutConstraint 对象的功能。为了防止多次安装约束,对 ConstraintBuilder 对象的任何转换都将将新构建的 NSLayoutConstraint 对象的所有权转移到调用者,因此当 ConstraintBuilder 对象被销毁时,就没有 NSLayoutConstraint 对象要安装了。遗憾的是,这阻止了直接获取一个指向 自动安装 约束的指针

// The following constraint has not been automatically installed
NSLayoutConstraint *constraint = View(_avatar).left == View().left + 5.0;

这是一个小小的代价,因为自动安装行为可以很容易地被引发

[constraint install];

无论如何,这种 魔幻 的行为可能会被一些人不喜欢

编译器警告

遗憾的是,编译器/静态分析器很可能会尝试警告您,您的独立约束表达式未使用。

Unused result warning

防止这种情况的唯一方法是不启用该警告。您可以将约束表达式这样包裹

_Pragma( "clang diagnostic push" )
_Pragma( "clang diagnostic ignored \"-Wunused-value\" " )

View(_avatar).left == View().left + 5.0;

_Pragma( "clang diagnostic pop")

但这很丑陋。为了整理一下,我添加了几个用于包装约束的宏

BeginConstraints

View(_avatar).left == View().left + 5.0;

EndConstraints;

约束分组宏 BeginConstraintGroup()EndConstraintGroup 也会禁用这些警告。

语法

主要表达式语法相当固定,但添加标识符和优先级的方法可能发生变化。目前的代码允许使用 ^ 运算符和 , 运算符来添加这些内容。我对建议的改进持开放态度。

替代方案

有很多尝试改进默认约束语法的方法。以下是一些引起我注意的方法

安装

AutoLayoutDSL可通过CocoaPods[https://cocoapods.org.cn]获得,要安装,只需将以下行添加到Podfile中

pod AutoLayoutDSL

作者

大卫·惠斯顿 [email protected]

致谢

约束表达式是我自己的设计,但是约束识别、分组和自动安装的理念都要归功于艾丽卡·桑德和她优秀著作《iOS 自动布局揭秘》。这些特性相关的代码大多是对这本书中包含样例代码的改编,具体可在此处找到[链接地址]

许可

AutoLayoutDSL 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。