AsyncDisplayKit is now Texture! LEARN MORE

Texture

Layout Specs

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

  • 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.

    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 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:

    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 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.

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

    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: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.

    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.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.

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

    Swift Objective-C
    - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
    {
      ...
      // ASLayoutSpec as spacer
      ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
      spacer.style.flexGrow = 1.0;
    
      stack.children = @[imageNode, spacer, textNode];
      ...
    }
    

    Edit on GitHub