用于快速 UI 布局的声明式且类型安全的框架
优势 | |
---|---|
🚀 | 使用属性链快速配置视图 |
🌈 | 使用最简单的 DSL轻松进行布局 |
🎁 | 用于 UIStackView 的 extra_most_usefull 助手 |
🧩 | 可扩展性! |
使用 Sourcery 驱动的代码生成,**所有可变属性**都以函数形式表示。
let myLabel = UILabel()
.numberOfLines(0)
.text("Hello buddy")
.backgroundColor(.blue)
.isHighlighted(true)
.borderWidth(1)
.borderColor(.cyan)
目前,以下类型支持属性链:UIView
、UIControl
、UILabel
、UIImageView
、UIScrollView
、UITextView
、UITableView
、UICollectionView
、UITextField
、UIButton
、UISlider
、UISwitch
、UIStackView
。
您还可以轻松地为其他类型生成函数 – 查看方法
可赋值给变量
class ViewController: UIViewController {
weak var myLabel: UILabel!
override func loadView() {
...
view.addSubview(
UILabel()
.numberOfLines(0)
.text("Voila")
// sets a reference of object that calls function(in this case, created UILabel instance) to passed variable
.assign(to: &myLabel)
)
...
}
}
基于闭包的动作和手势
UIControl()
.addAction(for: .valueChanged, { print("value changed") })
UIButton()
.title("Tap me")
.onTap({ print("didTap") }) // wrap .addAction(for: .touchUpInside, { .. })
UIView()
.onTapGesture({ print("Kek") })
.onLongTapGesture({ print("Cheburek") })
// ⚠️ Don't forget about ARC when use some parent view in action closure, to prevent retain cycle
预配置的堆栈初始化器
HorizontalStack([...]) // axis = .horizontal
HorizontalCenterStack([...]) // axis = .horizontal; alignment = .center
TopStack([...]) // axis = .horizontal; alignment = .top
BottomStack([...]) // axis = .horizontal; alignment = .bottom
VerticalStack([...]) // axis = .vertical
VerticalCenterStack([...]) // axis = .vertical; alignment = .center
LeftStack([...]) // axis = .vertical; alignment = .leading
RightStack([...]) // axis = .vertical; alignment = .trailing
声明式间距
VerticalStack([
SomeLabel(),
UIStackViewSpace(12)
SomeButton()
UIStackViewSpace(15)
UIImageView(image: ...)
SomeView()
...
])
@ArrangedViewBuilder 函数构建器
HorizontalStack {
AvatarImageView()
UIStackViewSpace(16)
TopStack {
NameLabel()
if user.isPremium {
PremiumMarkerView()
}
UIStackViewSpace(8)
HStackView(tagsViews)
}
HorizontalCenterStack {
ShareButton()
DisclosureButton()
}.spacing(4).distribution(.fillEquality)
}
您可以使用带有锚点约束的相同可链接样式来设置约束。
构建器的返回类型将是 AutoLayoutItem
– 常量指令的简单存储。 要构建并激活它们,只需调用 activate()
函数。
let myLabel = UILabel()
.numberOfLines(0) // -> UIView
...
.heightAnchor(0) // -> AutoLayoutItem (same below)
.topAnchor(16.from(anotherView.topAnchor))
.leftAnchor(24)
.rightAnchor(24.orLess.pririty(750))
.verticalAnchor(backgroundView)
.activate() // -> UIView (with applyed constraints)
constant
- Int/Float/CGFloat/...
target (仅限相对约束)
firstView.rightAnchor(24.from(secondView.leftAnchor))
subview.leftAnchor(16.to(superview.leftAnchor))
subview.leftAnchor(16)
等效于前面的示例relationType
Equal
(默认)orLess
15.orLess
; 15.to(secondView.leftAnchor).orLess
orGreater
15.orGreater
; 15.to(secondView.leftAnchor).orGreater
priorioty
15.priority(.defaultLow)
15.priority(250)
multiplier (仅限尺寸约束)
示例:headerView.heightAnchor(backgroundView.multiplied(0.5).orLess)
最终公式
constant.from|to(_ target: NSLayoutAnchor).priority(NSLayoutPriority).orLess|orGreater
属性的顺序是**任意的**
15.from(secondView.topAnchor).orLess
20.orLess.to(secondView.bottomAnchor).priority(1000)
8.priority(.required).orGreater
额外的锚点
horizontalAnchor
– leftAnchor + rightAnchor
示例:subview.horizontalAnchor(16)
verticalAnchor
– topAnchor + bottomAnchor
示例:subview.verticalAnchor(16.orLess)
centerAnchor
- centerXAnchor + centerYAnchor
示例:avatarView.centerAnchor(backgroundView)
sizeAnchor
- widthAnchor + heightAnchor
示例:avatarView.sizeAnchor(60)
edgesAnchors(insets: UIEdgeInsets, to target: UIView?, priority: UILayoutPriority)
- left|top|right|bottom 锚点的组合。
layout(_ builder: (UIView) -> AutoLayoutItem)
- 自相关锚点
示例:myView.layout({ $0.heightAnchor($0.widthAnchor) })
private(set) weak var avatarView: UIImageView!
...
let profileView = UIView()
.backgroundColor(.gray)
.heightAnchor(100)
.add({
UIImageView()
.assign(to: &avatarView)
.contentMode(.scaleAspectFit)
.sizeAnchor(40)
.leftAnchor(16)
.verticalAnchor(0)
UILabel()
.numberOfLines(2)
.rightAnchor(0)
.leftAnchor(8.from(avatarView.leftAnchor).priority(.required))
})
.activate()
或者使用便利的初始化器
let profileView = UIView {
UIImageView()
.assign(to: &avatarView)
.contentMode(.scaleAspectFit)
.sizeAnchor(40)
.leftAnchor(16)
.verticalAnchor(0)
UILabel()
.numberOfLines(2)
.rightAnchor(0)
.leftAnchor(8.from(avatarView.leftAnchor).priority(.required))
}
.backgroundColor(.gray)
.heightAnchor(100)
.activate()
ℹ️ 注意:如果您将约束构建器添加到 UIView
(即,在调用 add(...)
之前未使用 anchor-chaining
),则约束激活将在添加到 superview
**之后立即发生**。 换句话说,activate()
函数的返回类型将是 Self
(即 UIView
)
let profileView = UIView()
.backgroundColor(.gray)
.add({
UIImageView()
.assign(to: &avatarView)
.contentMode(.scaleAspectFit)
.sizeAnchor(40)
.leftAnchor(16)
.verticalAnchor(0)
UILabel()
.numberOfLines(2)
.rightAnchor(0)
.leftAnchor(8.from(avatarView.leftAnchor).priority(.required))
}) // -> UIView (with already added subviews)
// and you can continue chainable-configuration (for example by specifying own anchors)
.heightAnchor(100) // -> AutoLayoutItem
.activate() // -> UIView
第一种方法 – 编写返回 self 的类型扩展函数
extension MyCustomView {
func myProperty(_ value: ValueType) -> Self {
self.myProperty = value
return self
}
}
第二种方法 – 使用 Sourcery 将 Chainable 模板应用于您的自定义视图 (查看教程)。
有**大量**的布局框架...
创建另一个框架的**主要目标**不是创建另一个框架,而是 *为快速编写布局代码制作工具
许多人都知道 SnapKit 是这些解决方案之一,但我决定制作一个**更具声明性和更简单的解决方案**。\
附注:在以前的版本中,该框架基于 DSL SnapKit,但现在拥有自己的、更类型安全的版本。
此 README
描述是您需要了解的全部内容,无需冗余文档。\
# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'DeclarativeLayoutKit'
end
替换 YOUR_TARGET_NAME
,然后在 Podfile
目录中键入
$ pod install
创建一个 Package.swift
文件。
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
dependencies: [
.package(url: "https://github.com/Ernest0-Production/DeclarativeLayoutKit.git", from: "3.0.2")
],
targets: [
.target(name: "YOUR_TARGET_NAME", dependencies: ["DeclarativeLayoutKit"])
]
)
DeclarativeLayoutKit 在 MIT 许可证下发布。 有关详细信息,请参见LICENSE。