Paralayout is a set of simple, useful, and straightforward utilities that enable pixel-perfect layout in iOS. Your designers will love you.
Swift Package Manager
To install Paralayout via Swift Package Manager, add the following to your Package.swift
:
dependencies: [
.package(name: "Paralayout", url: "https://github.com/square/Paralayout.git", from: "2.0.0"),
]
Bazel
To install Paralayout via Bazel, add the following to your MODULE.bazel
:
bazel_dep(name = "paralayout", version = "2.0.0")
Carthage
To install Paralayout via Carthage, add the following to your Cartfile
:
github "Square/Paralayout"
Paralayout is a set of à la carte utilities, of which you can use as much or as little functionality as you like.
The first basic set of layout utilities is around sizing views. Paralayout extends UIView.sizeThatFits(_:)
and UIView.sizeToFit()
to include constraints, which gives you more control over how your subviews are sized.
For example, say you have a header bar you want to always be the full width of your container view, but use its ideal height (its sizeThatFits(_:)
) clamped to the height of the container. You can easily achieve this in a single method call by combining the .fixedWidth
and .maxHeight
constraints.
headerBar.sizeToFit(
bounds.size,
constraints: [.fixedWidth, .maxHeight]
)
Once you have your subviews sized, the next basic action you often take is aligning your subviews. Paralayout provides a powerful set of alignment methods to align one view to another.
For example, you'll commonly want to align one subview to another subview with a certain amount of spacing between them. Let's say we want our first subview to be 16 pt beneath our second subview.
firstSubview.align(
.topCenter,
with: secondSubview,
.bottomCenter,
verticalOffset: 16
)
Alignments are automatically snapped to the nearest pixel, saving you from fuzzy edges on your views due to bad layout math.
There are also conveniences for aligning views to their superview, one of the most common alignment scenarios. For example, we can center a subview in our superview using this simple call:
someSubview.align(withSuperview: .center)
Paralayout also supports more complex alignment concepts for specialized use cases, such as aligning a label by its first line of text.
label.firstLineAlignmentProxy.align(
.leftCenter,
with: icon,
.rightCenter,
horizontalOffset: 8
)
In addition to simplifying the math for sizing and aligning views, Paralayout also builds layout abstractions on top of these simple concepts. View distribution solves the very common scenario of distributing views across a view, on either the vertical or horizontal axis.
For example, we can distribute a series of views within a container view. Here we'll put 16 pt of space between each subview and distribute any additional space equally above and below the subviews.
containerView.applyVerticalSubviewDistribution(
[
1.flexible,
titleLabel,
16.fixed,
bodyLabel,
16.fixed,
actionButton,
1.flexible,
]
)
Working with aspect ratios has traditionally involved some easy-to-mess-up math, but Paralayout's AspectRatio
type makes it a breeze. Create an aspect ratio from any size, rect, or width/height value, and use it to compute pixel-snapped frame rectangles.
videoPlayer.frame = AspectRatio.widescreen.rect(toFit: bounds, at: .topCenter, in: view)
Get the math right for multi-phase layout transitions and animations without tearing your hair out! Under the hood, Paralayout's Interpolation
type is simply a value between 0 and 1, but it makes computing that value, and deriving a new value from it, effortless.
// Determine how far we are into the transition of collapsing the header.
let headerCollapseAmount = Interpolation(of: header.bounds.height, from: maxHeaderHeight, to: minHeaderHeight)
// The icon shrinks to half its usual size...
let avatarSize = headerCollapseAmount.interpolate(from: 80, to: 40)
avatar.bounds.size = CGSize(width: avatarSize, height: avatarSize)
// ...as it completely fades out.
avatar.alpha = headerCollapseAmount.interpolate(from: 1, to: 0)
The extra space within a label above the "cap height" and below the "baseline" of its font is deterministic but non-obvious, especially at different scale factors. The simple LabelCapInsets
value type encapsulates that logic.
- iOS 13.0 or later
- Xcode 15.0 or later
- Swift 5.9
We’re glad you’re interested in Paralayout, and we’d love to see where you take it. Please read our contributing guidelines prior to submitting a pull request.