StackFrameLayout Distribution Modes in FrameLayoutKit: .top, .center, .bottom, and .fill Explained
StackFrameLayout distribution modes control cross-axis alignment, where .top, .center, and .bottom position children at the start, middle, or end of the perpendicular axis, while .fill stretches each child to occupy the full cross-axis dimension.
The StackFrameLayout class in the kennic/framelayoutkit repository provides the foundation for HStackLayout and VStackLayout. Understanding the distribution property and its four primary modes—.top, .center, .bottom, and .fill—is essential for controlling how child views align across the axis perpendicular to the stack's direction.
Understanding StackFrameLayout Distribution
The NKLayoutDistribution Enum
The distribution modes are defined in FrameLayout.swift as the NKLayoutDistribution enum. This enum is consumed by StackFrameLayout during its layoutSubviews() pass to compute the final targetFrame for each child. The enum values map directly to the alignment behavior applied to the cross-axis coordinate.
How Distribution Affects Layout
When StackFrameLayout performs layout, it first calculates the primary axis position (left-to-right for HStackLayout, top-to-bottom for VStackLayout). The distribution property then dictates the cross-axis coordinate:
- For horizontal stacks, distribution controls vertical alignment.
- For vertical stacks, distribution controls horizontal alignment.
This axis-specific behavior means the same enum value produces different visual results depending on whether the stack flows horizontally or vertically.
Distribution Mode Reference
.top Alignment
The .top mode aligns children to the leading edge of the cross-axis.
- Horizontal stack: Each child's
targetFrame.origin.yis set tocontainerFrame.minY, pinning the view to the top of the stack. - Vertical stack: Each child's
targetFrame.origin.xis set tocontainerFrame.minX, pinning the view to the left.
In StackFrameLayout.swift, the .top case simply assigns the minimum edge coordinate without modifying the child's size.
.center Alignment
The .center mode centers children along the cross-axis.
- Horizontal stack: The origin is calculated as
containerFrame.minY + (containerFrame.height - targetFrame.height) / 2. - Vertical stack: The origin is calculated as
containerFrame.minX + (containerFrame.width - targetFrame.width) / 2.
This ensures equal padding on both sides of the child view, placing it exactly in the middle of the available cross-axis space.
.bottom Alignment
The .bottom mode aligns children to the trailing edge of the cross-axis.
- Horizontal stack:
targetFrame.origin.y = containerFrame.maxY - targetFrame.height, placing the view at the bottom. - Vertical stack:
targetFrame.origin.x = containerFrame.maxX - targetFrame.width, placing the view at the right.
The implementation uses isInvertedAlignment to handle the .bottom and .right cases consistently, flipping the origin calculation to the container's maximum edge.
.fill Expansion
The .fill mode overrides the child's cross-axis dimension to match the container's full extent.
- Horizontal stack:
targetFrame.origin.y = containerFrame.minYandtargetFrame.size.height = containerFrame.height. - Vertical stack:
targetFrame.origin.x = containerFrame.minXandtargetFrame.size.width = containerFrame.width.
Unlike the alignment modes, .fill modifies the child's size rather than just its position, ensuring every child stretches to fill the available cross-axis space.
Implementation Details
layoutSubviews Logic
The core logic resides in StackFrameLayout.layoutSubviews(). The method iterates over frameLayouts and, based on the current axis and distribution, computes targetFrame for each child.
For example, the .top case for a horizontal axis:
case .top:
targetFrame.origin.y = containerFrame.minY
And the .fill case:
case .fill:
targetFrame.origin.y = containerFrame.minY
targetFrame.size.height = containerFrame.height
Axis-Specific Behavior
The same NKLayoutDistribution value produces different visual results depending on axis:
- Horizontal axis (
HStackLayout): Distribution controls vertical positioning. - Vertical axis (
VStackLayout): Distribution controls horizontal positioning.
This is why .top in a horizontal stack aligns to the top edge, while in a vertical stack it aligns to the left edge (the "top" of the cross-axis).
Code Examples
import FrameLayoutKit
// 1. Top-aligned horizontal stack (items pinned to top)
let topStack = HStackLayout {
$0.distribution = .top // ⇢ each item’s top edge touches the stack’s top edge
$0.spacing = 8
$0.add(UIView()) // item 1
$0.add(UIView()) // item 2
}
// 2. Center-aligned horizontal stack (items vertically centered)
let centerStack = HStackLayout {
$0.distribution = .center
$0.spacing = 8
$0.add(UIView())
$0.add(UIView())
}
// 3. Bottom-aligned horizontal stack (items pinned to bottom)
let bottomStack = HStackLayout {
$0.distribution = .bottom
$0.spacing = 8
$0.add(UIView())
$0.add(UIView())
}
// 4. Fill distribution (items stretch to full height)
let fillStack = HStackLayout {
$0.distribution = .fill
$0.spacing = 8
$0.add(UIView()) // height will match stack height
$0.add(UIView())
}
// Vertical stack example – distribution controls horizontal alignment
let vStack = VStackLayout {
$0.distribution = .center // items centered horizontally
$0.spacing = 12
$0.add(UILabel())
$0.add(UIButton())
}
All four horizontal stacks behave identically in the primary direction (left‑to‑right). The only visual difference comes from the chosen distribution mode.
Summary
- StackFrameLayout distribution modes determine cross‑axis alignment for children in
HStackLayoutandVStackLayout. .toppins children to the start of the cross‑axis (top for horizontal stacks, left for vertical stacks)..centercenters children along the cross‑axis with equal padding on both sides..bottompins children to the end of the cross‑axis (bottom for horizontal stacks, right for vertical stacks)..fillstretches each child to occupy the entire cross‑axis dimension, modifying the child’s size rather than just its position.
Frequently Asked Questions
What is the default distribution mode for StackFrameLayout?
The default value for the distribution property is .top (or its axis‑appropriate equivalent). This means that unless you explicitly set a different mode, children will align to the top edge in a horizontal stack or the left edge in a vertical stack.
Does the .fill distribution respect a child view’s intrinsic content size?
No. When you set distribution = .fill, StackFrameLayout explicitly overrides the child’s cross‑axis dimension to match the container’s full height or width. The child’s intrinsic size is ignored for that axis, although the primary‑axis size (width for horizontal stacks, height for vertical stacks) may still be determined by the child’s content or explicit constraints.
How does distribution differ from alignment in FrameLayoutKit?
In FrameLayoutKit, distribution is a property of StackFrameLayout that controls the cross‑axis positioning of multiple children relative to the stack’s bounds. Alignment (often handled by individual FrameLayout instances or the alignment property) typically refers to how a single view’s content is aligned within its own frame. Distribution operates at the stack level; alignment operates at the individual frame level.
Can I mix distribution modes within the same StackFrameLayout?
No. The distribution property is applied uniformly to every child in the stack. If you need different cross‑axis alignments for specific items, you should wrap those items in a nested FrameLayout or use a ZStackLayout with manual positioning, or compose multiple StackFrameLayout instances with different distributions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →