DSL 是什么?
DSL 其实是 Domain Specific Language 的缩写,中文翻译为领域特定语言(下简称 DSL);而与 DSL 相对的就是 GPL,这里的 GPL 并不是我们知道的开源许可证,而是 General Purpose Language 的简称,即通用编程语言,也就是我们非常熟悉的 Objective-C、Java、Python 以及 C 语言等等。
A specialized computer language designed for a specific task.
为了解决某一类任务而专门设计的计算机语言。
- 总的来说:DSL 基于一门语言,专门解决某一个问题。适合声明式,规则明确的场景,类似
SnapKit
使用LayoutAnchor来布局
self.view .addSubview(label)
label.text = "测试"
label.backgroundColor = UIColor.orange;
NSLayoutConstraint.activate([
label.topAnchor.constraint(
equalTo: view.bottomAnchor,
constant: 20
),
label.leadingAnchor.constraint(
equalTo: view.leadingAnchor,
constant: 40
),
label.widthAnchor.constraint(
lessThanOrEqualTo: view.widthAnchor,
constant: -20
)
])
使用DSL后,代码就成了
label.layout {
$0.top.equal(to: view.topAnchor, offsetBy: 90)
$0.leading.equal(to: view.leadingAnchor,offsetBy: 100)
$0.width.lessThanOrEqual(to: view.widthAnchor, offsetBy: -40)
}
布局代码少了很多,符号更加直观了,拥有新的语法糖了
如何构造一个简单的布局DSL
- 建立功能协议
LayoutAnchor
protocol LayoutAnchor {
func constraint(equalTo anchor: Self,
constant: CGFloat) -> NSLayoutConstraint
func constraint(greaterThanOrEqualTo anchor: Self,
constant: CGFloat) -> NSLayoutConstraint
func constraint(lessThanOrEqualTo anchor: Self,
constant: CGFloat) -> NSLayoutConstraint
}
extension NSLayoutAnchor: LayoutAnchor {}
2.建立一个上层类LayoutProxy
class LayoutProxy {
// 在原生布局方法上,封装一层
lazy var leading = property(with: view.leadingAnchor)
lazy var trailing = property(with: view.trailingAnchor)
lazy var top = property(with: view.topAnchor)
lazy var bottom = property(with: view.bottomAnchor)
lazy var width = property(with: view.widthAnchor)
lazy var height = property(with: view.heightAnchor)
private let view: UIView
fileprivate init(view: UIView) {
self.view = view
}
private func property<A: LayoutAnchor>(with anchor: A) -> LayoutProperty<A> {
return LayoutProperty(anchor: anchor)
}
}
- 增加一个结构体
LayoutProperty,LayoutProperty包了个遵守LayoutAnchor的属性anchor
struct LayoutProperty<Anchor: LayoutAnchor> {
fileprivate let anchor: Anchor
}
extension LayoutProperty {
func equal(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0) {
anchor.constraint(equalTo: otherAnchor,
constant: constant).isActive = true
}
func greaterThanOrEqual(to otherAnchor: Anchor,
offsetBy constant: CGFloat = 0) {
anchor.constraint(greaterThanOrEqualTo: otherAnchor,
constant: constant).isActive = true
}
func lessThanOrEqual(to otherAnchor: Anchor,
offsetBy constant: CGFloat = 0) {
anchor.constraint(lessThanOrEqualTo: otherAnchor,
constant: constant).isActive = true
}
}
- 采用闭包,建立上下文环境,封装代码
extension UIView {
func layout(using closure: (LayoutProxy) -> Void) {
translatesAutoresizingMaskIntoConstraints = false
closure(LayoutProxy(view: self))
}
}
大功告成
label.layout{ make in
make.top.equal(to: view.topAnchor, offsetBy: 90)
make.leading.equal(to: view.leadingAnchor,offsetBy: 100)
make.width.lessThanOrEqual(to: view.widthAnchor, offsetBy: -40)
}
你也可以直接写成
label.layout {
$0.top.equal(to: view.topAnchor, offsetBy: 90)
$0.leading.equal(to: view.leadingAnchor,offsetBy: 100)
$0.width.lessThanOrEqual(to: view.widthAnchor, offsetBy: -40)
}