Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Has cross-hierarchy coordinate space. Implementation performed on rect-based constraints. Fast, asynchronous, declarative, cacheable, extensible. Supported iOS, macOS, tvOS, Linux.
Performed by [LayoutBenchmarkFramework](https://github.com/lucdion/LayoutFrameworkBenchmark)Layout with CGLayout
built using layout-blocks. To combine blocks into single unit use LayoutScheme
entity (or other entities that has suffix Scheme
).
let subviewsScheme = LayoutScheme(blocks: [
// ... layout blocks
])
To define block for "view" element use LayoutBlock
entity, or just use convenience getter methods 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)
}
)
For understanding how need to built layout block, let's see layout process in LayoutBlock
.
For example we have this configuration:
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))
])
]
)
Layout anchors are limiters, that is oriented on frame properties (such as sides, size, position).
Any side-based anchors have three base implementations: alignment, limitation(cropping), pulling. Each this implementation have dependency on working space: inner and outer.
Size-based anchors are represented by two implementations: size, insets.
All layout anchors you can find in enum LayoutAnchor
.
To create associated layout constraints use protocol LayoutConstraintProtocol
.
Framework provides such default implementations:
LayoutConstraint
: simple associated constraint that usesvar frame
of passed element to constrain source rect. Use him to build dependency on external workspace.AdjustLayoutConstraint
: associated constraint to adjust size of source space. Only elements conform toprotocol AdjustableLayoutElement
can use it.ContentLayoutConstraint
: associated constraint that uses internal bounds to constrain, defined in 'layoutBounds' property ofprotocol LayoutElement
. Use it if you need to create dependency on internal workspace. For example, element insideUIScrollView
.BaselineLayoutConstraint
: associated constraint that depended on base line. Only elements conform toprotocol TextPresentedElement
can use it.AnonymConstraint
: constraint to restrict source space independently from external environment.MutableLayoutConstraint
: Layout constraint that creates possibility to change active state You can find all this constraints through convenience functions in related elements. Use him to build layout blocks.
In common case, adjust constraints should be apply after any other constraints (but not always).
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))
])
Each layout-block has methods for layout, take snapshot and applying snapshot. Consequently you may use layout-blocks for direct layout, background layout and cached layout:
// 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()
}
For implementing custom layout entities and save strong typed code, use static func build(_ base: Conformed) -> Self
method.
Framework provides LayoutGuide
as analogue UILayoutGuide. It has possible to generate views and add them to hierarchy.
To create layout element placeholders use LayoutPlaceholder
class or him element specific subclasses (LayerPlaceholder
, ViewPlaceholder
).
Also you can use placeholder based on UILayoutGuide
: UIViewPlaceholder
, to apply in Autolayout environment.
All examples of code you can see in Example
folder.
For more details, see documentation and example project.
I will be happy your feedback, advices and pull requests. For more information read here
See here
To run the example project, clone the repo, and run pod install
from the Example directory first.
Xcode 8.3+
CGLayout is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "CGLayout"
k-o-d-e-n, koden.u8800@gmail.com
CGLayout is available under the MIT license. See the LICENSE file for more info.