Layout API Sizing
The easiest way to understand the compound dimension types in the Layout API is to see all the units in relation to one another.
Values (CGFloat, ASRelativeDimension)
ASRelativeDimension
is essentially a normal CGFloat with support for representing either a point value, or a % value. It allows the same API to take in both fixed values, as well as relative ones.
ASRelativeDimension is used to set the flexBasis
property on a child of an ASStackLayoutSpec
. The flexBasis property specifies the initial size in the stack dimension for this object, where the stack dimension is whether it is a horizontal or vertical stack.
When a relative (%) value is used, it is resolved against the size of the parent. For example, an item with 50% flexBasis will ultimately have a point value set on it at the time that the stack achieves a concrete size.
Constructing ASRelativeDimensions
ASDimension.h
contains 3 convenience functions to construct an ASRelativeDimension
. It is easiest to use function that corresponds to the type (top 2 functions).
ASRelativeDimensionMakeWithPoints(CGFloat points);
ASRelativeDimensionMakeWithPercent(CGFloat percent);
ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value);
ASRelativeDimension Example
PIPlaceSingleDetailNode
uses flexBasis to set 2 child nodes of a horizontal stack to share the width 40 / 60:
leftSideStack.flexBasis = ASRelativeDimensionMakeWithPercent(0.4f);
self.detailLabel.flexBasis = ASRelativeDimensionMakeWithPercent(0.6f);
[horizontalStack setChildren:@[leftSideStack, self.detailLabel]];
Sizes (CGSize, ASRelativeSize)
ASRelativeSize
is similar to a CGSize, but its width and height may represent either a point or percent value. In fact, their unit type may even be different from one another. ASRelativeSize
doesn’t have a direct use in the Layout API, except to construct an ASRelativeSizeRange
.
-
an
ASRelativeSize
consists of a.width
and.height
that are eachASRelativeDimensions
. -
the type of the width and height are independent; either one individually, or both, may be a point or percent value. (e.g. you could specify that an ASRelativeSize that has a height in points, but a variable % width)
Constructing ASRelativeSizes
ASRelativeSize.h
contains 2 convenience functions to construct an ASRelativeSize
. If you don’t need to support relative (%) values, you can construct an ASRelativeSize
with just a CGSize.
ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height);
ASRelativeSizeMakeWithCGSize(CGSize size);
Size Ranges (ASSizeRange, ASRelativeSizeRange)
Because the layout spec system allows flexibility with elements growing and shrinking, we sometimes need to provide limits / boundaries to its flexibility.
There are two size range types, but in essence, both contain a minimum and maximum size and that are used to influence the result of layout measurements.
In the Pinterest code base, the minimum size seems to be only necessary for stack specs in order to determine how much space to fill in between the children. For example, with buttons in a nav bar, we don’t want them to stack as closely together as they can fit — rather a minimum width, as wide as the screen, is specified and causes the stack to add spacing to satisfy that constraint.
It’s much more common that the “max” constraint is what matters, though. This is the case when text is wrapping or truncating - it’s encountering the maximum allowed width. Setting a minimum width for text doesn’t actually do anything—the text can’t be made longer—unless it’s in a stack, and spacing is added around it.
ASSizeRange
UIKit doesn’t provide a structure to bundle a minimum and maximum CGSize. So ASSizeRange
was created to support a minimum and maximum CGSize pair.
The constrainedSize
that is passed as an input to layoutSpecThatFits:
is an ASSizeRange
.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
ASRelativeSizeRange
ASRelativeSizeRange
is essentially a minimum and maximum size pair, that are used to constrain the size of a layout object. The minimum and maximum sizes must support both point and relative sizes, which is where our friend the ASRelativeSize comes in. Hence, an ASRelativeSizeRange consists of a minimum and maximum ASRelativeSize
.
ASRelativeSizeRange is used to set the sizeRange
property on a child of an ASStaticLayoutSpec
. If specified, the child’s size is restricted according to this size.
ASSizeRange vs. ASRelativeSizeRange
Why do we pass a ASSizeRange *constrainedSize
to a node’s layoutSpecThatFits:
function, but a ASRelativeSizeRange
for the .sizeRange
property on an element provided as a child of a layout spec?
It’s pretty rare that you need the percent feature for a .sizeRange feature, but it’s there to make the API as flexible as possible. The input value of the constrainedSize that comes into the argument, has already been resolved by the parent’s size. It may have been influenced by a percent type, but has always be converted by that point into points.
Constructing ASRelativeSizeRange
ASRelativeSize.h
contains 4 convenience functions to construct an ASRelativeSizeRange
from the various smaller units.
-
Percentage and point values can be combined. E.g. you could specify that an object is a certain height in points, but a variable percentage width.
-
If you only care to constrain the min / max or width / height, you can pass in
CGFLOAT_MIN
,CGFLOAT_MAX
,constrainedSize.max.width
, etc
Most of the time, relative values are not needed for a size range and the design requires an object to be forced to a particular size (min size = max size = no range). In this common case, you can use:
ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact);
Sizing Conclusion
Here we have our original table, which has been annotated to show the uses of the various units in the Layout API.
It’s worth noting that that there’s a certain flexibility to be able to use so many powerful options with a single API - flexBasis and sizeRange can be used to set points and percentages in different directions. However, since the majority of do not use the full set of options, we should adjust the API so that the powerful capabilities are a slightly more hidden.