Yet another AutoLayout abstraction framework that halps to simplify managing NSLayoutConstraints.
LayoutChain has simple intuitive syntax and decreased compilation time of views layout setup. Easy access to creating single constraint and features to setup own UIStackView with a little amount of constraints.
The idea was to create lightweight wrapper on AutoLayout without lossing access to native API. Other frameworks may have a lot of generics and operators overload that can significantly increase the time for type-checking during compilation. Some frameworks has complex API to construct layout but even do not has simple functions to take a single constraint for its managing.
- iOS 9.0+ / tvOS 10.0+
- Xcode 12.0+
- Swift 5.0+
Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
Xcode 12+ is required to build LayoutChain using Swift Package Manager.
To integrate LayoutChain into your Xcode project using Swift Package Manager, add it to the dependencies value of your Package.swift
:
dependencies: [
.package(url: "https://github.com/romdevios/LayoutChain.git", .upToNextMajor(from: "0.1.0"))
]
Or add dependency manually in Xcode. File -> Swift Packages -> Add Package Dependency... then enter the package URL 'https://github.com/romdevios/LayoutChain.git' and click Next button.
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
To integrate LayoutChain into your Xcode project using CocoaPods, specify it in your Podfile
:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
target '<Your Target Name>' do
pod 'LayoutChain'
end
Then, run the following command:
$ pod install --repo-update
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with Homebrew using the following command:
$ brew update
$ brew install carthage
To integrate LayoutChain into your Xcode project using Carthage, specify it in your Cartfile
:
github "romdevios/LayoutChain"
Run $ carthage update --use-xcframeworks
and drag the built .xcframework
bundles from Carthage/Build
into the "Frameworks and Libraries" section of your application’s Xcode project. If you are using Carthage for an application, select "Embed & Sign", otherwise "Do Not Embed".
Setup ViewController
import LayoutChain
class ViewController: UIViewController {
let container = UIView()
let view2 = UIView()
let view3 = UIView()
let view4 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
container.backgroundColor = .systemTeal
view2.backgroundColor = .systemYellow
view3.backgroundColor = .systemBlue
view4.backgroundColor = .systemIndigo
view.addSubview(container)
container.addSubview(view2)
container.addSubview(view3)
container.addSubview(view4)
layoutBuilder()
}
func layoutBuilder() {
// ...
}
}
func layoutBuilder() {
container.layoutChain
.centerX()
.centerY()
.width(to: view.widthAnchor * 0.8 - (20 * CGFloat(2))) // explicit type hack to speed up type-check
.height(to: 100)
view2.layoutChain
.top(inset: 20)
.leading(inset: 20)
.trailing(inset: 20)
view3.layoutChain
.leading(to: view2)
.bottom(inset: 20)
.height(to: 30)
view4.layoutChain
.trailing(to: view2)
.top(to: view3.topAnchor)
.bottom(to: view3)
.width(to: view3.widthAnchor * 2)
heightConstraint = view2.layoutChain.height(to: 20).build()
view2.layoutChain.bottomWith(view3.topAnchor, to: view3.trailingAnchor -- view4.leadingAnchor * 2)
}
UIView and UILayoutGuid extended by three properties that return one of helper. All this helpers confirms to LayoutBuilders for each orientation with set of functions for each anchor type.
With UIView().layoutOne
you can call function of builder and it return created and activated NSLayoutConstraint.
var heightConstraint: NSLayoutConstraint!
// ...
heightConstraint = box.layoutOne.height(to: 50)
// ...
heightConstraint.constrant = 200
With UIView().layoutChain
each function of builder will return LayoutChain object back and you can call it multiple times.
After all anchors is setted up you can leave this "chain" and come to configure a next view, LayoutChain will automaticaly activate constraints after deinit. But also you could build chain and store its result and deactivate or remove all constraints from the batch.
var layoutBatch: LayoutConstraintBatch!
// ...
layoutBatch = box.layoutChain
.width(to: 50)
.height(to: 50)
.build()
// ...
layoutBatch.deactivate()
Also LayoutChain has additional functionality:
box.layoutChain
.equalSize(to: containerView) // set widthAnchor and heightAnchor equaly
.padding(to: containerView, insets: UIEdgeInsets()) // arrange box into containerView with specified insets from edges
.center() // make center constraints to superview
With UIView().layoutStack(axis: Axis, _ items: UIView...)
you have additional chain functions to arrange items like in UIStackView.
box.layoutStack(axis: .vertical, view1, view2, view3)
.stackSpacing(16) // to make spacing between items equal to 16
.stackEqualSpacing() // to keep spacing between items equal
.stackInsets(UIEdgeInsets(top: 16, bottom: 16, left: 16, right: 16)) // insets from box view
.stackAlignment(.center()) // to align all elements in center of box
.stackDistributionEqual() // to keep equal size for all elements in selected stack orientation
.stackElementsWidth(constant: 200) // will set width constraint for all elements
.stackElementsHeight(constant: 200) // will set height constraint for all elements
Example
container.layoutChain
.centerX()
.centerY()
.width(to: view.widthAnchor * 0.8)
.height(to: 100)
container.layoutStack(axis: .horizontal, view2, view3, view4)
.stackInsets(.init(top: 4, left: 8, bottom: 4, right: 8))
.stackDistributionEqual()
.stackSpacing(8)
To add multiplication or constant to constraint you can use overloaded operators +
, -
and *
for dimension anchors.
box.layoutOne.top(to: container.topAnchor + 32)
box.layoutOne.width(to: box.heightAnchor * 2 - 10)
box.layoutOne.width(to: box.heightAnchor * (1/3)) // but be careful with associativity
In addition to standard anchors, there are other features here.
box.layoutChain
.aspect(ratio: 0.5) // to make width depend on height with ratio
.inheritWidth(multiplier: 0.8, constant: -16) // to depend on width of superview (also inheritHeight here)
.before(of: view, spacing: 8) // to constrain box trailing to view leading
.after(of: view, spacing: 8) // to constrain box leading to view trailing
.above(of: view) // box bottom to view top
.below(of: view) // box top to view bottom
For composite anchors it is special operator --
to make demension anchor from two xAxis or yAxis anchors.
view1.leftAnchor.anchorWithOffset(to: view2.rightAnchor) // native approach
view1.leftAnchor -- view2.rightAnchor // LayoutChain featur
To use this anchor in builder it is alternate functions for each xAxis and yAxis anchors. For example, to keep distance between view3.trailing and view4.leading twice smaller then distance between box.bottom and view2.top just add the following line of code:
box.layoutChain
.bottomWith(view2.topAnchor, to: view3.trailingAnchor -- view4.leadingAnchor * 2)