How the flexibleRatio Property Functions with Multiple Flexible FrameLayouts in StackFrameLayout
The flexibleRatio property determines how StackFrameLayout distributes remaining space among flexible children, using an internal autoFill algorithm that normalizes ratios to sum to 1.0 while treating -1 values as auto-fill placeholders that equally divide leftover space.
The flexibleRatio property is central to proportional sizing in the FrameLayoutKit Swift framework. When multiple FrameLayout instances inside a StackFrameLayout are marked as flexible, the property works in concert with the isFlexible flag and the autoFill(total:) normalization method to allocate available space according to the kennic/framelayoutkit source code.
Core Concepts Behind Space Distribution
Three components work together to handle flexible sizing in stacks:
-
isFlexible– A Boolean flag defined inFrameLayout.swift(public var isFlexible = false) that marks a frame as eligible to receive a portion of the remaining stack space. -
flexibleRatio– ACGFloatproperty inFrameLayout.swift(public var flexibleRatio: CGFloat = -1) representing the desired proportion of remaining space. The default value of-1signals auto-fill behavior. -
autoFill(total:)– An Array extension method implemented inStackFrameLayout.swift(lines 32-45) that normalizes an array of ratios so the sum equals 1.0, replacing-1placeholders with equal shares of any remaining proportion.
The Layout Algorithm
When StackFrameLayout calculates its layout, it processes flexible children in three distinct phases:
Collecting Flexible Frames
During the layout pass, the code first separates flexible frames from fixed-size ones. In StackFrameLayout.swift (lines 52-60), the layout iterates through activeFrameLayouts and collects any frame where isFlexible is true:
var flexibleFrames = [FrameLayout]()
for frameLayout in activeFrameLayouts {
if frameLayout.isEmpty { continue }
if frameLayout.isFlexible {
flexibleFrames.append(frameLayout)
continue
}
// handle fixed-size frames
}
Fixed-size frames are laid out first, consuming their defined space and leaving a remaining content area for the flexible frames to share.
Normalizing Ratios with autoFill
After identifying flexible frames, the layout extracts their flexibleRatio values and normalizes them. In StackFrameLayout.swift (lines 74-78), this occurs via:
let ratio = flexibleFrames.map { $0.flexibleRatio }.autoFill(total: flexibleFrameCount)
The autoFill(total:) extension (lines 32-45) performs the following normalization:
- Pads missing entries – If fewer ratios exist than frames,
-1placeholders are appended. - Counts placeholders – Calculates how many frames use auto-fill (
flexibleRatioCount). - Calculates remaining proportion – Determines
remainingRatio = (1.0 – sum(of explicit ratios)) / flexibleRatioCount. - Replaces placeholders – Substitutes all
-1values with the calculatedremainingRatio.
This guarantees the resulting ratio array always sums to 1.0, regardless of how many explicit or auto-fill values were provided.
Applying the Calculated Ratios
Finally, the layout applies these normalized ratios to the remaining content width (or height for vertical stacks). In StackFrameLayout.swift (lines 78-86), the code multiplies the available space by each ratio:
let contentWidth = contentSize.width - totalSpace - remainingSpace
var ratioIndex = 0
flexibleFrames.forEach {
let ratioValue = ratio[ratioIndex]
let cellWidth = contentWidth * ratioValue
frameContentSize = CGSize(width: cellWidth, height: contentSize.height)
frameContentSize = $0.sizeThatFits(frameContentSize)
ratioIndex += 1
}
The same logic applies to vertical stacks (lines 60-71), using contentHeight and cellHeight instead.
Behavior with Multiple Flexible Frames
When multiple flexible frames exist in the same stack, the interaction between explicit ratios and auto-fill values follows predictable rules:
-
All default values (-1) – Each flexible frame receives an equal share of the remaining space (
1.0 / flexibleFrameCount). -
Mixed explicit and auto-fill – Frames with explicit ratios (e.g.,
0.25,0.5) receive their exact proportion first. The remaining proportion is divided equally among all frames using the default-1value. -
Explicit ratios exceeding 1.0 – The
autoFillmethod does not clamp or error-check; if explicit ratios sum to greater than 1.0, the total allocated space will exceed the container bounds.
Practical Implementation Examples
The following examples demonstrate how to configure multiple flexible frames in horizontal and vertical stacks:
// Horizontal stack with mixed ratios
let hStack = HStackLayout {
$0.spacing = 8
// Fixed-width element
$0.add(UILabel()).fixedWidth(80)
// Flexible frame with auto-fill (receives 2/3 of remaining space)
$0.add(UIView()).flexible()
// Flexible frame with explicit 0.33 ratio (receives 1/3 of remaining space)
$0.add(UIView()).flexible(ratio: 0.33)
}
// Vertical stack with default auto-fill
let vStack = VStackLayout {
$0.spacing = 10
// Two flexible frames sharing space equally (0.5 each)
$0.add(UIView()).flexible()
$0.add(UIView()).flexible()
}
Assuming a 300-point width for hStack with 8-point spacing after the 80-point label, the flexible frames would receive approximately 141 points and 70 points respectively, based on their normalized ratios.
Summary
-
The
flexibleRatioproperty inFrameLayout.swiftdefaults to-1, indicating auto-fill behavior where the frame accepts an equal share of leftover space. -
StackFrameLayout.swiftimplements space distribution through theautoFill(total:)Array extension (lines 32-45), which normalizes ratios to sum to 1.0 regardless of input values. -
Fixed-size frames are laid out first; remaining space is then divided among flexible frames according to their normalized ratios during the layout pass (lines 52-86).
-
Explicit ratios are honored before auto-fill calculations, allowing precise control over relative sizing while maintaining flexible layout capabilities.
Frequently Asked Questions
What happens if all flexible frames use the default flexibleRatio of -1?
When all flexible frames retain the default flexibleRatio of -1, the autoFill algorithm assigns each frame an equal proportion of the remaining space. For three flexible frames, each receives a normalized ratio of 0.333 (or exactly 1/3 of the available content width or height).
How does StackFrameLayout handle a mix of explicit ratios and auto-fill values?
The layout first allocates space to frames with explicit flexibleRatio values (e.g., 0.25, 0.5). It then calculates the remaining proportion (1.0 minus the sum of explicit ratios) and divides that remainder equally among all frames marked with the default -1 value. This ensures explicit ratios maintain their weight while auto-fill frames absorb any leftover space.
What occurs if explicit flexibleRatios sum to greater than 1.0?
The autoFill implementation does not validate or clamp ratio sums. If explicit ratios exceed 1.0, the normalized array will reflect these values directly, causing the combined flexible frames to request more space than is available. This results in layout overflow where the total width or height exceeds the container's bounds.
Does flexibleRatio behave differently in horizontal versus vertical stacks?
The behavior is identical in both orientations. The only difference is the dimension being calculated: horizontal stacks use contentWidth and cellWidth (lines 78-86 in StackFrameLayout.swift), while vertical stacks use contentHeight and cellHeight (lines 60-71). The autoFill normalization and ratio application logic remains the same regardless of stack direction.
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 →