GetLaid is a lean framework for laying out complex user interfaces through elegant code.
- Why Oh Why?
- Install
- Add Subviews and Layout Guides
- Constrain Positions
- Constrain Multiple Positions
- Constrain Dimensions
- Constrain Both Dimensions
GetLaid has some advantages even over classic simple frameworks like PureLayout:
- âś… Readability
- Functions are of the principle form "[constrained aspect] constrain to [constraining aspect]"
- This semantic naming makes auto completion more valuable, as it quickly and meaningfully narrows down the available options.
- All functions have the prefix
constrain
which expresses best what they really do: They constrain some attributes and return the resulting constraints. - PureLayout is much more convoluted with its 6 different function prefixes:
autoPin
,autoAlign
,autoMatch
,autoCenter
,autoSet
andautoConstrain
.
- âś… Brevity
- Shorter lines of code with less function arguments (see comparison below)
- âś… Applicable to Layout Guides
- âś… Easy Relative Layouting
- Relative positioning:
item1.constrainToParentLeft(at: 0.5)
- Relative sizing:
item1.constrain(to: item2.width.at(0.3))
- Aspect ratio:
item.constrainAspectRatio(to: 16/9)
- Relative positioning:
- âś… Easy Positioning of Items Next to Each Other
item1.constrain(above: item2, gap: 10)
item1.constrain(after: item2)
(localized via leading / trailing)
- âś… Modern Swift Under the Hood
- No Objective-c
- Extensive use of Layout Anchors
Well, that would be insane.
Programmatic AutoLayout without any such frameworks was never hard. It's all about creating objects of NSLayoutConstraint
, which has only one powerful initializer.
Since iOS 9.0 and macOS 10.11, we also have NSLayoutAnchor
, which adds a native abstraction layer on top of NSLayoutConstraint
, further reducing the need for any AutoLayout wrappers at all.
At this point, all an AutoLayout wrapper can do is to make code even more meaningful, readable and succinct at the point of use. GetLaid does exactly that.
Modern AutoLayout wrappers like SnapKit are almost too clever for the simple task at hand. A SnapKit example:
box.snp.makeConstraints { (make) -> Void in
make.width.height.equalTo(50)
make.center.equalTo(self.view)
}
Classic AutoLayout wrappers like PureLayout, have easier syntax but are still wordy:
box.autoSetDimensions(to: CGSize(width: 50, height: 50))
box.autoCenterInSuperView()
GetLaid trims AutoLayout code even further down to the essence. Compare for yourself:
item1.autoPinEdgesToSuperviewEdges()
item1.autoPinEdge(toSuperviewEdge: .top)
item1.autoSetDimension(.width, toSize: 42)
item1.autoPinEdge(.left, to: .left, of: item2)
item1.autoAlignAxis(.vertical, toSameAxisOf: item2)
item1.autoSetDimensions(to: CGSize(width: 82, height: 42))
item1.autoPinEdge(.bottom, to: .top, of: item2, withOffset: -20)
item1.autoSetDimension(.height, toSize: 64, relation: .greaterThanOrEqual)
item1.autoPinEdgesToSuperviewEdges(with: NSEdgeInsetsZero, excludingEdge: .top)
item1.autoConstrainAttribute(.left, to: .right, of: parent, withMultiplier: 0.5)
item1.autoConstrainAttribute(.width, to: .height, of: item1, withMultiplier: 16/9)
item1.autoPinEdgesToSuperViewEdges(with: NSEdgeInsets(top: 10, left: 0, bottom: 0, right: 0))
item1.constrainToParent()
item1.constrainToParentTop()
item1.width.constrain(to: 42)
item1.constrain(to: item2.left)
item1.constrain(to: item2.centerX)
item1.constrain(to: 82, 42)
item1.constrain(above: item2, gap: 20)
item1.height.constrain(to: .min(64))
item1.constrainToParentButTop()
item1.constrainLeftToParent(at: 0.5)
item1.constrainAspectRatio(to: 16/9)
item1.constrainToParent(topInset: 10)
So, which is prettier, mh?
If you can spare fancyness but appreciate readability, GetLaid might be for you.
With the Swift Package Manager, you can just add the GetLaid package via Xcode (11+).
Or you manually adjust the Package.swift file of your project:
// swift-tools-version:5.1
import PackageDescription
let package = Package(
name: "MyApp",
dependencies: [
.package(url: "https://github.com/flowtoolz/GetLaid.git",
.upToNextMajor(from: "3.0.0"))
],
targets: [
.target(name: "MyAppTarget",
dependencies: ["GetLaid"])
]
)
Then run $ swift build
or $ swift run
.
With Cocoapods, adjust your Podfile:
target "MyAppTarget" do
pod "GetLaid", "~> 3.0"
end
Then run $ pod install
.
Finally, in your Swift files:
import GetLaid
The generic function addForAutoLayout
adds a subview and prepares it for AutoLayout. It returns the subview it takes as its exact type. Use this function to add subviews:
class List: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
header.constrainToParentExcludingBottom()
}
private lazy var header = addForAutoLayout(Header()) // of type Header
}
If you don't use addForAutoLayout
, remember to set translatesAutoresizingMaskIntoConstraints = false
on the views you incorporate in AutoLayout.
There's also a helper function for adding a new layout guide to a view:
let guide = view.addLayoutGuide()
You generally call constrain
on exactly the thing you want to constrain:
item1.left.constrain(to: item2.centerX)
All layout attributes can be used in that way, while baselines are not available on layout guides.
If source and target refer to the same position/anchor, you may omit one of them. These are equivalent:
item1.left.constrain(to: item2.left)
item1.left.constrain(to: item2)
item1.constrain(to: item2.left)
You may modify the constrain target and also chain these modifications:
item1.constrain(to: item2.left.offset(8))
item1.constrain(to: item2.left.min) // >= item2.left
item1.constrain(to: item2.left.max) // <= item2.left
item1.constrain(to: item2.left.at(0.5)) // at 0.5 of item2.left
item1.constrain(to: item2.left.min.offset(8))
You may constrain multiple positions at once:
item1.constrain(to: item2.allButTop(leadingOffset: 5, // leading, bottom, trailing
bottomOffset: -5))
item1.constrain(to: item2.center) // centerX, centerY
item1.constrain(to: item2.all) // all edges
item1.constrain(to: item2) // shorthand for .all
Available position target combinations are:
all
allButTop
allButLeading
allButLeft
allButBottom
allButTrailing
allButRight
center
All of them take offsets as arguments for exactly the constrained positions, in counter-clockwise order.
You constrain width and height just like positions:
item1.width.constrain(to: item2.height)
As with positions, you can omit anchors, modify the target, and chain modifications:
item1.constrain(to: item2.height.at(0.6).min) // >= 60% of item2.height
You can constrain a dimension to a constant size. These are equivalent:
item.width.constrain(to: .size(100))
item.width.constrain(to: 100)
Omit the dimension to constrain both dimensions to the same constant. These are also equivalent:
item.constrain(to: .size(100)) // square with edge length 100
item.constrain(to: 100) // same
Modify the constant size target like any other target, for one or both dimensions:
item.width.constrain(to: .size(100).max) // width <= 100
item.constrain(to: .size(100).max) // width, height <= 100
There's a shorter notation for minimum and maximum constants. These are equivalent:
item.constrain(to: .size(100).max) // width, height <= 100
item.constrain(to: .max(100)) // same
The size
target combines width and height. It works fully equivalent to those single dimensions:
item1.constrain(to: item2.size.min) // at least as big as item2
A size target can also represent a constant size. These are equivalent:
item.constrain(to: .size(100, 50)) // size target with constants
item.constrain(to: 100, 50) // same
And there's also a shorthand notation for minimum and maximum size. These are equivalent:
item.constrain(to: .size(100, 50).min) // at least 100 by 50
item.constrain(to: .min(100, 50)) // same
Finally, there's a shorthand for constraining aspect ratio. These are equivalent:
videoView.width.constrain(to: videoView.height.at(16 / 9))
videoView.constrainAspectRatio(to: 16 / 9)
- separate core API that uses
constrain(to:
functions from convenience API - system spacing
- find out whether there's still a difference between sibling spacing and parent spacing
- safe areas, parent
- constrain to parent (convenience funcs)
- constrain neighbours