AsyncDisplayKit

Layout Specs

The following ASLayoutSpec subclasses can be used to compose simple or very complex layouts.

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.

Swift Objective-C
// 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 ASDK, 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:

Swift Objective-C
- (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 AsyncDisplayKit as it does in CSS on the web, with a few exceptions. The defaults are different, there is no flex parameter and flexGrow and flexShrink only supports a boolean value.


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.

Swift Objective-C
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ...
  UIEdgeInsets *insets = UIEdgeInsetsMake(10, 10, 10, 10);
  ASInsetLayoutSpec *headerWithInset = 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.

When using Automatic Subnode Management with the 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).
Swift Objective-C
- (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.

When using Automatic Subnode Management with the 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).
Swift Objective-C
- (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:

Swift Objective-C
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
  return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
                                                    sizingOptions:ASRelativeLayoutSpecSizingOptionDefault
                                                            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.

Swift Objective-C
- (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.

Swift Objective-C
- (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:

Swift Objective-C
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  CGSize maxConstrainedSize = constrainedSize.max;

  // Layout all nodes absolute in a static layout spec
  guitarVideoNode.layoutPosition = CGPointMake(0, 0);
  guitarVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width, maxConstrainedSize.height / 3.0));

  nicCageVideoNode.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
  nicCageVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0));

  simonVideoNode.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height - (maxConstrainedSize.height / 3.0));
  simonVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width/2, maxConstrainedSize.height / 3.0));

  hlsVideoNode.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height / 3.0);
  hlsVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0));

  return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]];
}

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.

Swift Objective-C
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ...
  // ASLayoutSpec as spacer
  ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
  spacer.flexGrow = true;

  stack.children = @[imageNode, spacer, textNode];
  ...
}

Edit on GitHub