Layout Specs
The following ASLayoutSpec
subclasses can be used to compose simple or very complex layouts.
ASWrapperLayoutSpec
ASStackLayoutSpec
ASInsetLayoutSpec
ASOverlayLayoutSpec
ASBackgroundLayoutSpec
ASCenterLayoutSpec
ASRatioLayoutSpec
ASRelativeLayoutSpec
ASAbsoluteLayoutSpec
ASCornerLayoutSpec
You may also subclass ASLayoutSpec
in order to make your own, custom layout specs.
ASWrapperLayoutSpec
ASWrapperLayoutSpec
is a simple ASLayoutSpec
subclass that can wrap a ASLayoutElement
and calculate the layout of the child based on the size set on the layout element.
ASWrapperLayoutSpec
is ideal for easily returning a single subnode from -layoutSpecThatFits:
. Optionally, this subnode can have sizing information set on it. However, if you need to set a position in addition to a size, use ASAbsoluteLayoutSpec
instead.
// return a single subnode from layoutSpecThatFits:
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
return [ASWrapperLayoutSpec wrapperWithLayoutElement:_subnode];
}
// set a size (but not position)
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_subnode.style.preferredSize = CGSizeMake(constrainedSize.max.width,
constrainedSize.max.height / 2.0);
return [ASWrapperLayoutSpec wrapperWithLayoutElement:_subnode];
}
ASStackLayoutSpec (Flexbox Container)
Of all the layoutSpecs in Texture, ASStackLayoutSpec
is the most useful and powerful. ASStackLayoutSpec
uses the flexbox algorithm to determine the position and size of its children. Flexbox is designed to provide a consistent layout on different screen sizes. In a stack layout you align items in either a vertical or horizontal stack. A stack layout can be a child of another stack layout, which makes it possible to create almost any layout using a stack layout spec.
ASStackLayoutSpec
has 7 properties in addition to its <ASLayoutElement>
properties:
direction
. Specifies the direction children are stacked in. If horizontalAlignment and verticalAlignment were set, they will be resolved again, causing justifyContent and alignItems to be updated accordingly.spacing
. The amount of space between each child.horizontalAlignment
. Specifies how children are aligned horizontally. Depends on the stack direction, setting the alignment causes either justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. Thus, it is preferred to those properties.verticalAlignment
. Specifies how children are aligned vertically. Depends on the stack direction, setting the alignment causes either justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. Thus, it is preferred to those properties.justifyContent
. It defines the alignment along the main axis.alignItems
. Orientation of children along cross axis.flexWrap
. Whether children are stacked into a single or multiple lines. Defaults to single line.alignContent
. Orientation of lines along cross axis if there are multiple lines.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:6.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_iconNode, _countNode]];
// Set some constrained size to the stack
mainStack.style.minWidth = ASDimensionMakeWithPoints(60.0);
mainStack.style.maxHeight = ASDimensionMakeWithPoints(40.0);
return mainStack;
}
Flexbox works the same way in Texture as it does in CSS on the web, with a few exceptions. For example, the defaults are different and there is no flex
parameter. See Web Flexbox Differences for more information.
ASInsetLayoutSpec
During the layout pass, the ASInsetLayoutSpec
passes its constrainedSize.max
CGSize
to its child, after subtracting its insets. Once the child determines it’s final size, the inset spec passes its final size up as the size of its child plus its inset margin. Since the inset layout spec is sized based on the size of it’s child, the child must have an instrinsic size or explicitly set its size.
If you set INFINITY
as a value in the UIEdgeInsets
, the inset spec will just use the intrinisic size of the child. See an example of this.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
...
UIEdgeInsets *insets = UIEdgeInsetsMake(10, 10, 10, 10);
ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:textNode];
...
}
ASOverlayLayoutSpec
ASOverlayLayoutSpec
lays out its child (blue), stretching another component on top of it as an overlay (red).
The overlay spec’s size is calculated from the child’s size. In the diagram below, the child is the blue layer. The child’s size is then passed as the constrainedSize
to the overlay layout element (red layer). Thus, it is important that the child (blue layer) must have an intrinsic size or a size set on it.
ASOverlayLayoutSpec
, the nodes may sometimes appear in the wrong order. This is a known issue that will be fixed soon. The current workaround is to add the nodes manually, with the overlay layout element (red) must added as a subnode to the parent node after the child layout element (blue).
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:backgroundNode overlay:foregroundNode];
}
ASBackgroundLayoutSpec
ASBackgroundLayoutSpec
lays out a component (blue), stretching another component behind it as a backdrop (red).
The background spec’s size is calculated from the child’s size. In the diagram below, the child is the blue layer. The child’s size is then passed as the constrainedSize
to the background layout element (red layer). Thus, it is important that the child (blue layer) must have an intrinsic size or a size set on it.
ASOverlayLayoutSpec
, the nodes may sometimes appear in the wrong order. This is a known issue that will be fixed soon. The current workaround is to add the nodes manually, with the child layout element (blue) must added as a subnode to the parent node after the child background element (red).
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
return [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:foregroundNode background:backgroundNode];
}
Note: The order in which subnodes are added matters for this layout spec; the background object must be added as a subnode to the parent node before the foreground object. Using ASM does not currently guarantee this order!
ASCenterLayoutSpec
ASCenterLayoutSpec
centers its child within its max constrainedSize
.
If the center spec’s width or height is unconstrained, it shrinks to the size of the child.
ASCenterLayoutSpec
has two properties:
centeringOptions
. Determines how the child is centered within the center spec. Options include: None, X, Y, XY.sizingOptions
. Determines how much space the center spec will take up. Options include: Default, minimum X, minimum Y, minimum XY.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
sizingOptions:ASCenterLayoutSpecSizingOptionDefault
child:subnode]
}
ASRatioLayoutSpec
ASRatioLayoutSpec
lays out a component at a fixed aspect ratio which can scale. This spec must have a width or a height passed to it as a constrainedSize as it uses this value to scale itself.
It is very common to use a ratio spec to provide an intrinsic size for ASNetworkImageNode
or ASVideoNode
, as both do not have an intrinsic size until the content returns from the server.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
// Half Ratio
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(100, 100));
return [ASRatioLayoutSpec ratioLayoutSpecWithRatio:0.5 child:subnode];
}
ASRelativeLayoutSpec
Lays out a component and positions it within the layout bounds according to vertical and horizontal positional specifiers. Similar to the “9-part” image areas, a child can be positioned at any of the 4 corners, or the middle of any of the 4 edges, as well as the center.
This is a very powerful class, but too complex to cover in this overview. For more information, look into ASRelativeLayoutSpec
’s -calculateLayoutThatFits:
method + properties.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
...
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
ASRelativeLayoutSpec *relativeSpec = [ASRelativeLayoutSpec relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart
verticalPosition:ASRelativeLayoutSpecPositionStart
sizingOption:ASRelativeLayoutSpecSizingOptionDefault
child:foregroundNode]
ASBackgroundLayoutSpec *backgroundSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:relativeSpec background:backgroundNode];
...
}
ASAbsoluteLayoutSpec
Within ASAbsoluteLayoutSpec
you can specify exact locations (x/y coordinates) of its children by setting their layoutPosition
property. Absolute layouts are less flexible and harder to maintain than other types of layouts.
ASAbsoluteLayoutSpec
has one property:
sizing
. Determines how much space the absolute spec will take up. Options include: Default, and Size to Fit. Note that the Size to Fit option will replicate the behavior of the oldASStaticLayoutSpec
.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
CGSize maxConstrainedSize = constrainedSize.max;
// Layout all nodes absolute in a static layout spec
guitarVideoNode.style.layoutPosition = CGPointMake(0, 0);
guitarVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width, maxConstrainedSize.height / 3.0);
nicCageVideoNode.style.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
nicCageVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
simonVideoNode.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height - (maxConstrainedSize.height / 3.0));
simonVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width/2, maxConstrainedSize.height / 3.0);
hlsVideoNode.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height / 3.0);
hlsVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]];
}
ASCornerLayoutSpec
ASCornerLayoutSpec
is a new convenient layout spec for fast corner element layout. The easy way to position an element in corner is to use declarative code expression rather than manual coordinate calculation, and ASCornerLayoutSpec can achieve this goal.
ASCornerLayoutSpec
takes good care of its own size calculation. The best scenario to explain this would be the case that adding a small badge view at the corner of user’s avatar image and there is no need to worry about the fact that little-exceeded badge frame (which out of avatar image frame) may affect the whole layout size. By default, the size of corner element will not be added to layout size, only if you manually turn on the wrapsCorner
property.
ASCornerLayoutSpec
is introduced from version 2.7 and above.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
...
// Layout the center of badge to the top right corner of avatar.
ASCornerLayoutSpec *cornerSpec = [ASCornerLayoutSpec cornerLayoutSpecWithChild:self.avatarNode corner:self.badgeNode location:ASCornerLayoutLocationTopRight];
// Slightly shift center of badge inside of avatar.
cornerSpec.offset = CGPointMake(-3, 3);
...
}
ASLayoutSpec
ASLayoutSpec
is the main class from that all layout spec’s are subclassed. It’s main job is to handle all the children management, but it also can be used to create custom layout specs. Only the super advanced should want / need to create a custom subclasses of ASLayoutSpec
though. Instead try to use provided layout specs and compose them together to create more advanced layouts.
Another use of ASLayoutSpec
is to be used as a spacer in a ASStackLayoutSpec
with other children, when .flexGrow
and/or .flexShrink
is applied.
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
...
// ASLayoutSpec as spacer
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.style.flexGrow = 1.0;
stack.children = @[imageNode, spacer, textNode];
...
}