强大的自动布局框架,可以管理 UIView(NSView)、CALayer 和非渲染视图。具有跨层级坐标空间。实现基于基于矩形的约束。
快速、异步、声明式、可缓存、可扩展。支持 iOS、macOS、tvOS、Linux。
使用布局块构建的 CGLayout 布局。要将块组合成单个单元,请使用 LayoutScheme 实体(或其他带有 Scheme 后缀的实体)。
let subviewsScheme = LayoutScheme(blocks: [
// ... layout blocks
])
要为“视图”元素定义块,请使用 LayoutBlock 实体,或直接使用便捷的 getter 方法 func layoutBlock(with:constraints:)。
titleLabel.layoutBlock(
with: Layout(x: .center(), y: .top(5), width: .scaled(1), height: .fixed(120)),
constraints: [
logoImageView.layoutConstraint(for: [.bottom(.limit(on: .inner))])
]
)
/// or using anchors
titleLabel.layoutBlock(
with: Layout(x: .center(), y: .top(5), width: .scaled(1), height: .fixed(120)),
constraints: { anchors in
anchors.top.equal(to: logoImageView.layoutAnchors.bottom)
}
)
为了理解如何构建布局块,让我们看看 LayoutBlock 中的布局过程。例如,我们有这样的配置
LayoutBlock(
with: layoutElement,
layout: Layout(x: .left(10), y: .top(10), width: .boxed(10), height: .boxed(10)),
constraints: [
element1.layoutConstraint(for: [
.bottom(.limit(on: .outer)), .right(.limit(on: .inner))
]),
element2.layoutConstraint(for: [
.right(.limit(on: .outer)), .bottom(.limit(on: .inner))
])
]
)
布局锚点是限制器,它面向 frame 属性(例如边、大小、位置)。
任何基于边的锚点都有三个基本实现:对齐、限制(裁剪)、拉伸。每个实现都依赖于工作空间:内部和外部。
基于大小的锚点由两个实现表示:大小、内边距。
您可以在 enum LayoutAnchor 中找到所有布局锚点。
要创建关联的布局约束,请使用 protocol LayoutConstraintProtocol。
框架提供以下默认实现
LayoutConstraint:简单的关联约束,它使用传递元素的 var frame 来约束源矩形。使用它来构建对外部工作空间的依赖。AdjustLayoutConstraint:用于调整源空间大小的关联约束。只有符合 protocol AdjustableLayoutElement 的元素才能使用它。ContentLayoutConstraint:关联约束,它使用内部 bounds 进行约束,在 protocol LayoutElement 的 'layoutBounds' 属性中定义。如果您需要创建对内部工作空间的依赖,请使用它。例如,UIScrollView 内部的元素。AnonymConstraint:独立于外部环境限制源空间的约束。MutableLayoutConstraint:允许更改活动状态的布局约束。在通常情况下,调整约束应在任何其他约束之后应用(但并非总是如此)。
weatherLabel.layoutBlock(
with: Layout(x: .left(10), y: .top(), width: .scaled(1), height: .scaled(1)),
constraints: [
weatherImageView.layoutConstraint(for: [.top(.limit(.inner)), .right(.limit(.outer)), .height()]),
weatherLabel.adjustLayoutConstraint(for: [.width()])
]
)
AnonymConstraint(anchors: [
Inset(UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 15))
])
为了实现自定义布局实体并保存强类型代码,请使用 static func build(_ base: Conformed) -> Self 方法。
每个布局块都有用于布局、拍摄快照和应用快照的方法。因此,您可以将布局块用于直接布局、后台布局和缓存布局
// layout directly
layoutScheme.layout()
// layout in background
let bounds = view.bounds
DispatchQueue.global(qos: .background).async {
let snapshot = self.layoutScheme.snapshot(for: bounds)
DispatchQueue.main.sync {
self.layoutScheme.apply(snapshot: snapshot)
}
}
// cached layout
if UIDevice.current.orientation.isPortrait, let snapshot = portraitSnapshot {
layoutScheme.apply(snapshot: snapshot)
} else if UIDevice.current.orientation.isLandscape, let snapshot = landscapeSnapshot {
layoutScheme.apply(snapshot: snapshot)
} else {
layoutScheme.layout()
}
典型的 sizeThatFits(_:) 方法实现
func sizeThatFits(_ size: CGSize) -> CGSize {
let sourceRect = CGRect(origin: .zero, size: size)
let snapshot = scheme.snapshot(for: sourceRect)
return snapshot.frame
}
框架提供 LayoutGuide 作为 UILayoutGuide 的类似物。它可以生成视图并将它们添加到层级结构中。
LayoutGuide 可以用作不可见的限制器,也可以用作布局容器。
默认布局容器
StackLayoutGuide - 堆栈的简单实现。ScrollLayoutGuide - 具有与 UIScrollView 类似的接口。通过使用它,我们可以在任何地方启用滚动。LayoutPlaceholder - 可以延迟加载视图的单元素容器。具有 CALayer - LayerPlaceholder 和 UIView - ViewPlaceholder 的默认实现。UIViewPlaceholder - 基于 UILayoutGuide 的单元素容器。UILayouGuide 也采用了 LayoutElement 协议。因此,您可以安全地基于 UIView.safeAreaLayoutGuide 和其他对象构建约束。
要启用从右到左模式,请使用全局配置
CGLConfiguration.default.isRTLMode = true
有关更多详细信息,请参阅文档和示例项目。
请参阅这里
要运行示例项目,请克隆仓库,并首先从 Example 目录运行 pod install。
要运行示例项目,请克隆仓库,并首先从 linux_example 目录运行 swift run。
Swift 5
CGLayout 可通过 CocoaPods 获得。要安装它,只需将以下行添加到您的 Podfile
pod "CGLayout"
我将很高兴收到您的反馈、建议和 pull 请求。有关更多信息,请阅读 这里
Denis Koryttsev 邮箱:koden.u8800@gmail.com Twitter:https://twitter.com/K_o_D_e_N
CGLayout 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。