CGLayout

[已弃用] 已替换为新的实现 - LayoutUI

Version License Platform

强大的自动布局框架,可以管理 UIView(NSView)、CALayer 和非渲染视图。具有跨层级坐标空间。实现基于基于矩形的约束。
快速、异步、声明式、可缓存、可扩展。支持 iOS、macOS、tvOS、Linux。

通过 [LayoutBenchmarkFramework]( https://github.com/lucdion/LayoutFrameworkBenchmark) 执行

快速教程

使用布局块构建的 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,因此下一个布局块必须具有带有“视图”的约束,这些视图不会更改 frame。

布局锚点是限制器,它面向 frame 属性(例如边、大小、位置)。
任何基于边的锚点都有三个基本实现:对齐、限制(裁剪)、拉伸。每个实现都依赖于工作空间:内部和外部。
基于大小的锚点由两个实现表示:大小、内边距。
您可以在 enum LayoutAnchor 中找到所有布局锚点。

要创建关联的布局约束,请使用 protocol LayoutConstraintProtocol
框架提供以下默认实现

在通常情况下,调整约束应在任何其他约束之后应用(但并非总是如此)。

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

框架提供 LayoutGuide 作为 UILayoutGuide 的类似物。它可以生成视图并将它们添加到层级结构中。
LayoutGuide 可以用作不可见的限制器,也可以用作布局容器。
默认布局容器

UILayouGuide 也采用了 LayoutElement 协议。因此,您可以安全地基于 UIView.safeAreaLayoutGuide 和其他对象构建约束。

RTL

要启用从右到左模式,请使用全局配置

CGLConfiguration.default.isRTLMode = true

有关更多详细信息,请参阅文档和示例项目。

代码文档

请参阅这里

示例

macOS、iOS、tvOS

要运行示例项目,请克隆仓库,并首先从 Example 目录运行 pod install

Linux

要运行示例项目,请克隆仓库,并首先从 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 文件。