How the isJustified Property Evenly Distributes Child Views in StackFrameLayout
Setting isJustified to true in a StackFrameLayout calculates the remaining container space after initial layout and distributes it evenly as extra spacing between visible child views, filling the entire width or height without manual margin calculations.
The isJustified property in the FrameLayoutKit library provides automatic space distribution for iOS and macOS layouts. When enabled on a horizontal or vertical stack, it transforms leftover container space into equal gaps between child views, eliminating the need for complex constraint arithmetic in StackFrameLayout.
Understanding the isJustified Property in StackFrameLayout
StackFrameLayout supports multiple distribution modes including top, bottom, left, right, equal, split, and center alignment. The isJustified property adds a distinct behavior: instead of grouping items at one edge or centering them, it spreads visible children across the entire available axis.
When isJustified is enabled, the layout engine performs a three-step algorithm inside the layoutSubviews() method. This calculation determines how much extra space to inject between each visible frame, ensuring the first item touches the leading edge and the last item touches the trailing edge.
How isJustified Calculates Even Distribution
The distribution algorithm implemented in StackFrameLayout.swift follows a precise mathematical sequence to achieve even spacing.
Step 1: Calculate Remaining Space
After laying out all non-flexible items and applying standard spacing, the system determines the leftover container dimension:
let remainingWidth = containerFrame.width - usedSpace
let numberOfSpaces = numberOfVisibleFrames() - 1
For vertical stacks, this calculation uses remainingHeight against the container height instead.
Step 2: Validate the Justification Threshold
Justification only occurs when the leftover space exceeds the justifyThreshold property. This prevents micro-adjustments when insufficient space exists:
if remainingWidth > justifyThreshold, numberOfSpaces > 0 {
// Proceed with distribution
}
Step 3: Distribute Leftover Space Across Gaps
The algorithm divides the remaining space evenly across the gaps between visible frames. For left-aligned horizontal stacks (.top or .left), the implementation in StackFrameLayout.swift (lines 995-1014) calculates:
let extraValuePerSpace = (remainingWidth / CGFloat(numberOfSpaces)) + (spacing / CGFloat(numberOfSpaces))
var index = 1
for frameLayout in frameLayouts where frameLayout != firstFrame {
var rect = frameLayout.frame
rect.origin.x += extraValuePerSpace * CGFloat(index)
frameLayout.frame = rect
if !frameLayout.isEmpty { index += 1 }
}
For right-aligned stacks (.bottom or .right), the logic at lines 970-1005 applies the same calculation in reverse, subtracting the extra offset from each frame's origin.x using an inverted layout array.
Vertical justification follows identical logic but operates on the y coordinate within the else // if axis == .vertical branch of layoutSubviews().
Implementation Details in StackFrameLayout.swift
The core justification logic resides in the layoutSubviews() method of StackFrameLayout.swift. The implementation handles both horizontal and vertical axes through separate code paths that share the same mathematical principles.
Key implementation characteristics include:
- Left-to-right justification: Iterates forward through
frameLayouts, applying progressive offsets to distribute children across the width (lines 995-1014) - Right-to-left justification: Iterates backward through
invertedLayoutArray, subtracting offsets from the trailing edge inward (lines 970-1005) - Vertical axis: Mirrors the horizontal logic but adjusts
origin.yinstead oforigin.x
The isJustified property is exposed through chainable setters in Extensions/StackFrameLayout+Chainable.swift, allowing fluent configuration:
public func isJustified(_ value: Bool) -> Self {
self.isJustified = value
return self
}
Code Examples: Using isJustified in Practice
Horizontal Stack with Full-Width Distribution
This example creates a horizontal stack where three buttons spread evenly across the entire container width:
import FrameLayoutKit
let hStack = HStackLayout()
hStack.spacing = 8
hStack.isJustified = true
hStack.add([buttonA, buttonB, buttonC])
parentView.addSubview(hStack)
hStack.frame = parentView.bounds
// Result: First button at left edge, last button at right edge, equal gaps between
Vertical Stack with Justification Threshold
This configuration only applies justification when sufficient vertical space exists, preventing cramped distributions:
let vStack = VStackLayout()
vStack.isJustified = true
vStack.justifyThreshold = 20 // Minimum 20 points of leftover space required
vStack.spacing = 12
vStack.add([headerLabel, contentView, footerLabel])
parentView.addSubview(vStack)
vStack.frame = CGRect(x: 0, y: 0, width: 300, height: 600)
// Result: Labels and content spread evenly only if height exceeds content + 20pt
Chainable Configuration
Using the fluent API for concise setup:
let stack = HStackLayout()
.spacing(16)
.isJustified(true)
.justifyThreshold(10)
.add([view1, view2, view3])
Summary
- The
isJustifiedproperty inStackFrameLayoutenables automatic even distribution of child views across the full container width or height. - The algorithm calculates remaining space after standard layout, validates it against
justifyThreshold, and distributes leftovers evenly across gaps between visible frames. - Implementation resides in
StackFrameLayout.swiftwithin thelayoutSubviews()method, with separate handling for horizontal (lines 970-1014) and vertical axes. - The property supports both left-to-right and right-to-left layout directions through forward and inverted array iteration.
- Chainable setters in
StackFrameLayout+Chainable.swiftprovide fluent API configuration.
Frequently Asked Questions
What is the difference between isJustified and equal distribution in StackFrameLayout?
Equal distribution resizes child views themselves to make them identical widths or heights, while isJustified maintains each child's intrinsic size and instead adds extra spacing between views to fill the container. Justification preserves content aspect ratios and natural sizes, whereas equal distribution modifies the frames of the child views directly.
How does the justifyThreshold property affect layout behavior?
The justifyThreshold property acts as a minimum space requirement that must be exceeded before justification activates. If the remaining container space after initial layout is less than or equal to justifyThreshold, the layout reverts to standard alignment (such as left or top alignment) without distributing extra space. This prevents awkward micro-gaps when insufficient room exists for meaningful distribution.
Can isJustified be used with vertical stack layouts?
Yes, isJustified works identically for both horizontal and vertical axes. When applied to a vertical StackFrameLayout, the algorithm calculates remainingHeight instead of remainingWidth and distributes extra space evenly across the vertical gaps between child views. The implementation handles vertical justification in the else // if axis == .vertical branch of the layoutSubviews() method using the same mathematical approach as horizontal layouts.
Does isJustified work with hidden child views?
Yes, the justification algorithm specifically accounts for visibility. The calculation uses numberOfVisibleFrames() to determine gap counts and iterates through frameLayouts while checking !frameLayout.isEmpty before incrementing the distribution index. This ensures that hidden or empty views are excluded from the spacing calculation, and extra space is only distributed between actually visible child views.
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 →