` protocol. Any `ASLayoutable` object may be the child of a layoutSpec. ASLayoutable properties may be applied to `ASLayoutable` objects to create complex UI designs.
+
+### Single Child layoutSpecs
+
+
+
+ | LayoutSpec |
+ Description |
+
+
+ ASInsetLayoutSpec |
+ Applies an inset margin around a component. The object that is being inset must have an intrinsic size. |
+
+
+ ASOverlayLayoutSpec |
+ Lays out a component, stretching another component on top of it as an overlay. The underlay object must have an intrinsic size. Additionally, the order in which subnodes are added matters for this layoutSpec; the overlay object must be added as a subnode to the parent node after the underlay object. |
+
+
+ ASBackgroundLayoutSpec |
+ Lays out a component, stretching another component behind it as a backdrop. The foreground object must have an intrinsic size. The order in which subnodes are added matters for this layoutSpec; the background object must be added as a subnode to the parent node before the foreground object. |
+
+
+ ASCenterLayoutSpec |
+ Centers a component in the available space. The ASCenterLayoutSpec must have an intrinisic size. |
+
+
+ ASRatioLayoutSpec |
+ Lays out a component at a fixed aspect ratio (which can be scaled). This spec is great for objects that do not have an intrinisic size, such as ASNetworkImageNodes and ASVideoNodes. |
+
+
+ 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. |
+
+
+ ASLayoutSpec |
+ Can be used as a spacer in a stack spec with other children, when .flexGrow and/or .flexShrink is applied. This class can also be subclassed to create custom layout specs - advanced ASDK only! |
+
+
+
+### Multiple Child(ren) layoutSpecs
+
+The following layoutSpecs may contain one or more children.
+
+
+
+ | LayoutSpec |
+ Description |
+
+
+ ASStackLayoutSpec |
+ Allows you to stack components vertically or horizontally and specify how they should be flexed and aligned to fit in the available space. This is the most common layoutSpec. |
+
+
+ ASStaticLayoutSpec |
+ Allows positioning children at fixed offsets using the .sizeRange and .layoutPosition ASLayoutable properties. |
+
+
+
+# ASLayoutable Properties
+
+The following properties can be applied to both nodes _and_ `layoutSpec`s; both conform to the `ASLayoutable` protocol.
+
+### ASStackLayoutable Properties
+
+The following properties may be set on any node or `layoutSpec`s, but will only apply to those who are a **child of a stack** `layoutSpec`.
+
+
+
+ | Property |
+ Description |
+
+
+ CGFloat .spacingBefore |
+ Additional space to place before this object in the stacking direction. |
+
+
+ CGFloat .spacingAfter |
+ Additional space to place after this object in the stacking direction. |
+
+
+ BOOL .flexGrow |
+ If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? Used when attached to a stack layout. |
+
+
+ BOOL .flexShrink |
+ If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? Used when attached to a stack layout. |
+
+
+ ASRelativeDimension .flexBasis |
+ Specifies the initial size for this object, in the stack dimension (horizontal or vertical), before the flexGrow or flexShrink properties are applied and the remaining space is distributed. |
+
+
+ ASStackLayoutAlignSelf alignSelf |
+ Orientation of the object along cross axis, overriding alignItems. Used when attached to a stack layout. |
+
+
+ CGFloat .ascender |
+ Used for baseline alignment. The distance from the top of the object to its baseline. |
+
+
+ CGFloat .descender |
+ Used for baseline alignment. The distance from the baseline of the object to its bottom. |
+
+
+
+### ASStaticLayoutable Properties
+
+The following properties may be set on any node or `layoutSpec`s, but will only apply to those who are a **child of a static** `layoutSpec`.
+
+
+
+ | Property |
+ Description |
+
+
+ .sizeRange |
+ If specified, the child's size is restricted according to this ASRelativeSizeRange. Percentages are resolved relative to the static layout spec. |
+
+
+ .layoutPosition |
+ The CGPoint position of this object within its parent spec. |
+
+
+
+### Providing Intrinsic Sizes for Leaf Nodes
+
+AsyncDisplayKit's layout is recursive, starting at the layoutSpec returned from `layoutSpecThatFits:` and proceeding down until it reaches the leaf nodes included in any nested `layoutSpec`s.
+
+Some leaf nodes provide their own intrinsic size, such as `ASTextNode` or `ASImageNode`. An attributed string or an image have their own sizes. Other leaf nodes require an intrinsic size to be set.
+
+**Nodes that require the developer to provide an intrinsic size:**
+
+ - `ASDisplayNode` custom subclasses may provide their intrinisc size by implementing `calculateSizeThatFits:`.
+ - `ASNetworkImageNode` or `ASMultiplexImageNode` have no intrinsic size until the image is downloaded.
+ - `ASVideoNode` or `ASVideoNodePlayer` have no intrinsic size until the video is downloaded.
+
+
+To provide an intrinisc size for these nodes, you can set one of the following:
+
+ 1. implement `calculateSizeThatFits:` for **custom ASDisplayNode subclasses** only.
+ 2. set `.preferredFrameSize`
+ 3. set `.sizeRange` for children of **static** nodes only.
+
+
+Note that `.preferredFrameSize` is not considered by `ASTextNodes`. Also, setting .sizeRange on a node will override the node's intrinisic size provided by `calculateSizeThatFits:`.
+
+### Common Confusions
+
+There are two main confusions that developers have when using layoutSpecs
+
+ 1. Certain ASLayoutable properties only apply to children of stack nodes, while other properties only apply to children of static nodes. All ASLayoutable properties can be applied to any node or layoutSpec, however certain properties will only take effect depending on the type of the parent layoutSpec they are wrapped in. These differences are highlighted above in the ASStackLayoutable Properties and ASStaticLayoutable Properties sections.
+ 2. Have I set an intrinsic size for all of my leaf nodes?
+
+
+#### I set `.flexGrow` on my node, but it doesn't grow?
+
+Upward propogation of `ASLayoutable` properties is currently disabled. Thus, in certain situations, the `.flexGrow` property must be manually applied to the containers. Two common examples of this that we see include:
+
+- a node (with `flexGrow` enabled) is wrapped in a static layoutSpec, wrapped in a stack layoutSpec. **solution**: enable `flexGrow` on the static layoutSpec as well.
+- a node (with `flexGrow` enabled) is wrapped in an inset spec. **solution**: enable `flexGrow` on the inset spec as well.
+
+
+#### I want to provide a size for my image, but I don't want to hard code the size.
+
+#### Why won't my stack spec span the full width?
+
+#### Difference between `ASInsetLayoutSpec` and `ASOverlayLayoutSpec`
+
+An overlay spec requires the underlay object (object to which the overlay item will be applied) to have an intrinsic size. It will center the overlay object in the middle of this area.
+
+An inset spec requires its object to have an intrinsic size. It adds the inset padding to this size to calculate the final size of the inset spec.
+
+
+
+### Best Practices
+ - AsyncDisplayKit layout is called on a background thread. Do not access the device screen bounds, or any other UIKit methods in `layoutSpecThatFits:`.
+ - don't wrap everything in a staticLayoutSpec?
+ - avoid using preferred frame size for everything - won't respond nicely to device rotation or device sizing differences?
diff --git a/docs/_docs/automatic-layout-examples-2.md b/docs/_docs/automatic-layout-examples-2.md
new file mode 100755
index 00000000..eb55c2f5
--- /dev/null
+++ b/docs/_docs/automatic-layout-examples-2.md
@@ -0,0 +1,264 @@
+---
+title: Layout Examples
+layout: docs
+permalink: /docs/automatic-layout-examples-2.html
+prevPage: layout2-quickstart.html
+nextPage: layout2-layoutspec-types.html
+---
+
+Check out the layout specs example project to play around with the code below.
+
+## Simple Header with Left and Right Justified Text
+
+
+
+To create this layout, we will use a:
+
+- a vertical `ASStackLayoutSpec`
+- a horizontal `ASStackLayoutSpec`
+- `ASInsetLayoutSpec` to inset the entire header
+
+The diagram below shows the composition of the layout elements (nodes + layout specs).
+
+
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ // when the username / location text is too long,
+ // shrink the stack to fit onscreen rather than push content to the right, offscreen
+ ASStackLayoutSpec *nameLocationStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ nameLocationStack.style.flexShrink = 1.0;
+ nameLocationStack.style.flexGrow = 1.0;
+
+ // if fetching post location data from server,
+ // check if it is available yet and include it if so
+ if (_postLocationNode.attributedText) {
+ nameLocationStack.children = @[_usernameNode, _postLocationNode];
+ } else {
+ nameLocationStack.children = @[_usernameNode];
+ }
+
+ // horizontal stack
+ ASStackLayoutSpec *headerStackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+ spacing:40
+ justifyContent:ASStackLayoutJustifyContentStart
+ alignItems:ASStackLayoutAlignItemsCenter
+ children:@[nameLocationStack, _postTimeNode]];
+
+ // inset the horizontal stack
+ return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 10, 0, 10) child:headerStackSpec];
+}
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ let nameLocationStack = ASStackLayoutSpec.vertical()
+ nameLocationStack.style.flexShrink = 1.0
+ nameLocationStack.style.flexGrow = 1.0
+
+ if postLocationNode.attributedText != nil {
+ nameLocationStack.children = [userNameNode, postLocationNode]
+ } else {
+ nameLocationStack.children = [userNameNode]
+ }
+
+ let headerStackSpec = ASStackLayoutSpec(direction: .horizontal,
+ spacing: 40,
+ justifyContent: .start,
+ alignItems: .center,
+ children: [nameLocationStack, postTimeNode])
+
+ return ASInsetLayoutSpec(insets: UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10), child: headerStackSpec)
+}
+
+
+
+
+Rotate the example project from portrait to landscape to see how the spacer grows and shrinks.
+
+## Photo with Inset Text Overlay
+
+
+
+To create this layout, we will use a:
+
+- `ASInsetLayoutSpec` to inset the text
+- `ASOverlayLayoutSpec` to overlay the inset text spec on top of the photo
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ _photoNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);
+
+ // INIFINITY is used to make the inset unbounded
+ UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
+ ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_titleNode];
+
+ return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_photoNode overlay:textInsetSpec];
+}
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ let photoDimension: CGFloat = constrainedSize.max.width / 4.0
+ photoNode.style.preferredSize = CGSize(width: photoDimension, height: photoDimension)
+
+ // INFINITY is used to make the inset unbounded
+ let insets = UIEdgeInsets(top: CGFloat.infinity, left: 12, bottom: 12, right: 12)
+ let textInsetSpec = ASInsetLayoutSpec(insets: insets, child: titleNode)
+
+ return ASOverlayLayoutSpec(child: photoNode, overlay: textInsetSpec)
+}
+
+
+
+
+## Photo with Outset Icon Overlay
+
+
+
+To create this layout, we will use a:
+
+- `ASAbsoluteLayoutSpec` to place the photo and icon which have been individually sized and positioned using their `ASLayoutable` properties
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ _iconNode.style.preferredSize = CGSizeMake(40, 40);
+ _iconNode.style.layoutPosition = CGPointMake(150, 0);
+
+ _photoNode.style.preferredSize = CGSizeMake(150, 150);
+ _photoNode.style.layoutPosition = CGPointMake(40 / 2.0, 40 / 2.0);
+
+ return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit
+ children:@[_photoNode, _iconNode]];
+}
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ iconNode.style.preferredSize = CGSize(width: 40, height: 40);
+ iconNode.style.layoutPosition = CGPoint(x: 150, y: 0);
+
+ photoNode.style.preferredSize = CGSize(width: 150, height: 150);
+ photoNode.style.layoutPosition = CGPoint(x: 40 / 2.0, y: 40 / 2.0);
+
+ let absoluteSpec = ASAbsoluteLayoutSpec(children: [photoNode, iconNode])
+
+ // ASAbsoluteLayoutSpec's .sizing property recreates the behavior of ASDK Layout API 1.0's "ASStaticLayoutSpec"
+ absoluteSpec.sizing = .sizeToFit
+
+ return absoluteSpec;
+}
+
+
+
+
+
+
+## Simple Inset Text Cell
+
+
+
+To recreate the layout of a single cell as is used in Pinterest's search view above, we will use a:
+
+- `ASInsetLayoutSpec` to inset the text
+- `ASCenterLayoutSpec` to center the text according to the specified properties
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ UIEdgeInsets insets = UIEdgeInsetsMake(0, 12, 4, 4);
+ ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets
+ child:_titleNode];
+
+ return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY
+ sizingOptions:ASCenterLayoutSpecSizingOptionMinimumX
+ child:inset];
+}
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ let insets = UIEdgeInsets(top: 0, left: 12, bottom: 4, right: 4)
+ let inset = ASInsetLayoutSpec(insets: insets, child: _titleNode)
+
+ return ASCenterLayoutSpec(centeringOptions: .Y, sizingOptions: .minimumX, child: inset)
+}
+
+
+
+
+## Top and Bottom Separator Lines
+
+
+
+To create the layout above, we will use a:
+
+- a `ASInsetLayoutSpec` to inset the text
+- a vertical `ASStackLayoutSpec` to stack the two separator lines on the top and bottom of the text
+
+The diagram below shows the composition of the layoutables (layout specs + nodes).
+
+
+
+The following code can also be found in the `ASLayoutSpecPlayground` [example project]().
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ _topSeparator.style.flexGrow = 1.0;
+ _bottomSeparator.style.flexGrow = 1.0;
+
+ ASInsetLayoutSpec *insetContentSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 20, 20, 20) child:_textNode];
+
+ return [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
+ spacing:0
+ justifyContent:ASStackLayoutJustifyContentCenter
+ alignItems:ASStackLayoutAlignItemsStretch
+ children:@[_topSeparator, insetContentSpec, _bottomSeparator]];
+}
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ topSeparator.style.flexGrow = 1.0
+ bottomSeparator.style.flexGrow = 1.0
+ textNode.style.alignSelf = .center
+
+ let verticalStackSpec = ASStackLayoutSpec.vertical()
+ verticalStackSpec.spacing = 20
+ verticalStackSpec.justifyContent = .center
+ verticalStackSpec.children = [topSeparator, textNode, bottomSeparator]
+
+ return ASInsetLayoutSpec(insets:UIEdgeInsets(top: 60, left: 0, bottom: 60, right: 0), child: verticalStackSpec)
+}
+
+
+
diff --git a/docs/_docs/automatic-layout-examples.md b/docs/_docs/automatic-layout-examples.md
new file mode 100755
index 00000000..7b2b4139
--- /dev/null
+++ b/docs/_docs/automatic-layout-examples.md
@@ -0,0 +1,204 @@
+---
+title: Layout Examples
+layout: docs
+permalink: /docs/automatic-layout-examples.html
+prevPage: automatic-layout-containers.html
+nextPage: automatic-layout-debugging.html
+---
+
+Three examples in increasing order of complexity.
+#NSSpain Talk Example
+
+
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
+{
+ ASStackLayoutSpec *vStack = [[ASStackLayoutSpec alloc] init];
+
+ [vStack setChildren:@[titleNode, bodyNode];
+
+ ASStackLayoutSpec *hstack = [[ASStackLayoutSpec alloc] init];
+ hStack.direction = ASStackLayoutDirectionHorizontal;
+ hStack.spacing = 5.0;
+
+ [hStack setChildren:@[imageNode, vStack]];
+
+ ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5,5,5,5) child:hStack];
+
+ return insetSpec;
+}
+
+
+
+
+
+
+
+###Discussion
+
+#Social App Layout
+
+
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ // header stack
+ _userAvatarImageView.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); // constrain avatar image frame size
+
+ ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
+ spacer.flexGrow = YES;
+
+ ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
+ headerStack.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horizontal stack
+ headerStack.justifyContent = ASStackLayoutJustifyContentStart; // justify content to left side of header stack
+ headerStack.spacing = HORIZONTAL_BUFFER;
+
+ [headerStack setChildren:@[_userAvatarImageView, _userNameLabel, spacer, _photoTimeIntervalSincePostLabel]];
+
+ // header inset stack
+
+ UIEdgeInsets insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+ ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:headerStack];
+ headerWithInset.flexShrink = YES;
+
+ // vertical stack
+
+ CGFloat cellWidth = constrainedSize.max.width;
+ _photoImageView.preferredFrameSize = CGSizeMake(cellWidth, cellWidth); // constrain photo frame size
+
+ ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space
+
+ [verticalStack setChildren:@[headerWithInset, _photoImageView, footerWithInset]];
+
+ return verticalStack;
+}
+
+
+
+
+
+
+
+###Discussion
+
+Get the full ASDK project at examples/ASDKgram.
+
+#Social App Layout 2
+
+
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
+
+ ASLayoutSpec *textSpec = [self textSpec];
+ ASLayoutSpec *imageSpec = [self imageSpecWithSize:constrainedSize];
+ ASOverlayLayoutSpec *soldOutOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imageSpec
+ overlay:[self soldOutLabelSpec]];
+
+ NSArray *stackChildren = @[soldOutOverImage, textSpec];
+
+ ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
+ spacing:0.0
+ justifyContent:ASStackLayoutJustifyContentStart
+ alignItems:ASStackLayoutAlignItemsStretch
+ children:stackChildren];
+
+ ASOverlayLayoutSpec *soldOutOverlay = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:mainStack
+ overlay:self.soldOutOverlay];
+
+ return soldOutOverlay;
+}
+
+- (ASLayoutSpec *)textSpec {
+ CGFloat kInsetHorizontal = 16.0;
+ CGFloat kInsetTop = 6.0;
+ CGFloat kInsetBottom = 0.0;
+ UIEdgeInsets textInsets = UIEdgeInsetsMake(kInsetTop, kInsetHorizontal, kInsetBottom, kInsetHorizontal);
+
+ ASLayoutSpec *verticalSpacer = [[ASLayoutSpec alloc] init];
+ verticalSpacer.flexGrow = YES;
+
+ ASLayoutSpec *horizontalSpacer1 = [[ASLayoutSpec alloc] init];
+ horizontalSpacer1.flexGrow = YES;
+
+ ASLayoutSpec *horizontalSpacer2 = [[ASLayoutSpec alloc] init];
+ horizontalSpacer2.flexGrow = YES;
+
+ NSArray *info1Children = @[self.firstInfoLabel, self.distanceLabel, horizontalSpacer1, self.originalPriceLabel];
+ NSArray *info2Children = @[self.secondInfoLabel, horizontalSpacer2, self.finalPriceLabel];
+ if ([ItemNode isRTL]) {
+ info1Children = [[info1Children reverseObjectEnumerator] allObjects];
+ info2Children = [[info2Children reverseObjectEnumerator] allObjects];
+ }
+
+ ASStackLayoutSpec *info1Stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+ spacing:1.0
+ justifyContent:ASStackLayoutJustifyContentStart
+ alignItems:ASStackLayoutAlignItemsBaselineLast children:info1Children];
+
+ ASStackLayoutSpec *info2Stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+ spacing:0.0
+ justifyContent:ASStackLayoutJustifyContentCenter
+ alignItems:ASStackLayoutAlignItemsBaselineLast children:info2Children];
+
+ ASStackLayoutSpec *textStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
+ spacing:0.0
+ justifyContent:ASStackLayoutJustifyContentEnd
+ alignItems:ASStackLayoutAlignItemsStretch
+ children:@[self.titleLabel, verticalSpacer, info1Stack, info2Stack]];
+
+ ASInsetLayoutSpec *textWrapper = [ASInsetLayoutSpec insetLayoutSpecWithInsets:textInsets
+ child:textStack];
+ textWrapper.flexGrow = YES;
+
+ return textWrapper;
+}
+
+- (ASLayoutSpec *)imageSpecWithSize:(ASSizeRange)constrainedSize {
+ CGFloat imageRatio = [self imageRatioFromSize:constrainedSize.max];
+
+ ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:self.dealImageView];
+
+ self.badge.layoutPosition = CGPointMake(0, constrainedSize.max.height - kFixedLabelsAreaHeight - kBadgeHeight);
+ self.badge.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(0), ASRelativeDimensionMakeWithPoints(kBadgeHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(1), ASRelativeDimensionMakeWithPoints(kBadgeHeight)));
+ ASStaticLayoutSpec *badgePosition = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.badge]];
+
+ ASOverlayLayoutSpec *badgeOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imagePlace overlay:badgePosition];
+ badgeOverImage.flexGrow = YES;
+
+ return badgeOverImage;
+}
+
+- (ASLayoutSpec *)soldOutLabelSpec {
+ ASCenterLayoutSpec *centerSoldOutLabel = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
+ sizingOptions:ASCenterLayoutSpecSizingOptionMinimumXY child:self.soldOutLabelFlat];
+ ASStaticLayoutSpec *soldOutBG = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.soldOutLabelBackground]];
+ ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:soldOutBG];
+ ASBackgroundLayoutSpec *soldOutLabelOverBackground = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:centerSoldOutLabel background:centerSoldOut];
+ return soldOutLabelOverBackground;
+}
+
+
+
+
+
+
+
+###Discussion
+
+Get the full ASDK project at examples/CatDealsCollectionView.
diff --git a/docs/_docs/automatic-subnode-mgmt.md b/docs/_docs/automatic-subnode-mgmt.md
new file mode 100755
index 00000000..7432c180
--- /dev/null
+++ b/docs/_docs/automatic-subnode-mgmt.md
@@ -0,0 +1,178 @@
+---
+title: Automatic Subnode Management
+layout: docs
+permalink: /docs/automatic-subnode-mgmt.html
+prevPage: batch-fetching-api.html
+nextPage: inversion.html
+---
+
+Enabling Automatic Subnode Management (ASM) is required to use the Layout Transition API. However, apps that don't require animations can still benefit from the reduction in code size that this feature enables.
+
+When enabled, ASM means that your nodes no longer require `addSubnode:` or `removeFromSupernode` method calls. The presence or absence of the ASM node _and_ its subnodes is completely determined in its `layoutSpecThatFits:` method.
+
+### Example ###
+
+Consider the following intialization method from the PhotoCellNode class in ASDKgram sample app. This ASCellNode subclass produces a simple social media photo feed cell.
+
+In the "Original Code" we see the familiar `addSubnode:` calls in bold. In the "Code with ASM" (switch at top right of code block) these have been removed and replaced with a single line that enables ASM.
+
+By setting `.automaticallyManagesSubnodes` to `YES` on the `ASCellNode`, we _no longer_ need to call `addSubnode:` for each of the `ASCellNode`'s subnodes. These `subNodes` will be present in the node hierarchy as long as this class' `layoutSpecThatFits:` method includes them.
+
+
+
+ Code with ASM
+ Original Code
+
+
+
+- (instancetype)initWithPhotoObject:(PhotoModel *)photo;
+{
+ self = [super init];
+
+ if (self) {
+ _photoModel = photo;
+
+ _userAvatarImageNode = [[ASNetworkImageNode alloc] init];
+ _userAvatarImageNode.URL = photo.ownerUserProfile.userPicURL;
+ [self addSubnode:_userAvatarImageNode];
+
+ _photoImageNode = [[ASNetworkImageNode alloc] init];
+ _photoImageNode.URL = photo.URL;
+ [self addSubnode:_photoImageNode];
+
+ _userNameTextNode = [[ASTextNode alloc] init];
+ _userNameTextNode.attributedString = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
+ [self addSubnode:_userNameTextNode];
+
+ _photoLocationTextNode = [[ASTextNode alloc] init];
+ [photo.location reverseGeocodedLocationWithCompletionBlock:^(LocationModel *locationModel) {
+ if (locationModel == _photoModel.location) {
+ _photoLocationTextNode.attributedString = [photo locationAttributedStringWithFontSize:FONT_SIZE];
+ [self setNeedsLayout];
+ }
+ }];
+ [self addSubnode:_photoLocationTextNode];
+ }
+
+ return self;
+}
+
+
+
+- (instancetype)initWithPhotoObject:(PhotoModel *)photo;
+{
+ self = [super init];
+
+ if (self) {
+ self.automaticallyManagesSubnodes = YES;
+
+ _photoModel = photo;
+
+ _userAvatarImageNode = [[ASNetworkImageNode alloc] init];
+ _userAvatarImageNode.URL = photo.ownerUserProfile.userPicURL;
+
+ _photoImageNode = [[ASNetworkImageNode alloc] init];
+ _photoImageNode.URL = photo.URL;
+
+ _userNameTextNode = [[ASTextNode alloc] init];
+ _userNameTextNode.attributedString = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
+
+ _photoLocationTextNode = [[ASTextNode alloc] init];
+ [photo.location reverseGeocodedLocationWithCompletionBlock:^(LocationModel *locationModel) {
+ if (locationModel == _photoModel.location) {
+ _photoLocationTextNode.attributedString = [photo locationAttributedStringWithFontSize:FONT_SIZE];
+ [self setNeedsLayout];
+ }
+ }];
+ }
+
+ return self;
+}
+
+
+
+
+Several of the elements in this cell - `_userAvatarImageNode`, `_photoImageNode`, and `_photoLocationLabel` depend on seperate data fetches from the network that could return at any time. When should they be added to the UI?
+
+ASM knows whether or not to include these elements in the UI based on the information provided in the cell's `ASLayoutSpec`.
+
+
+An ASLayoutSpec completely describes the UI of a view in your app by specifying the hierarchy state of a node and its subnodes. An ASLayoutSpec is returned by a node from its layoutSpecThatFits: method.
+
+
+**It is your job to construct a `layoutSpecThatFits:` that handles how the UI should look with and without these elements.**
+
+Consider the abreviated `layoutSpecThatFits:` method for the `ASCellNode` subclass above.
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ headerSubStack.flexShrink = YES;
+ if (_photoLocationLabel.attributedString) {
+ [headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]];
+ } else {
+ [headerSubStack setChildren:@[_userNameLabel]];
+ }
+
+ _userAvatarImageNode.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); // constrain avatar image frame size
+
+ ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
+ spacer.flexGrow = YES;
+
+ UIEdgeInsets avatarInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
+ ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageNode];
+
+ ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
+ headerStack.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horizontal stack
+ headerStack.justifyContent = ASStackLayoutJustifyContentStart; // justify content to the left side of the header stack
+ [headerStack setChildren:@[avatarInset, headerSubStack, spacer]];
+
+ // header inset stack
+ UIEdgeInsets insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+ ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:headerStack];
+
+ // footer inset stack
+ UIEdgeInsets footerInsets = UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER);
+ ASInsetLayoutSpec *footerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:footerInsets child:_photoCommentsNode];
+
+ // vertical stack
+ CGFloat cellWidth = constrainedSize.max.width;
+ _photoImageNode.preferredFrameSize = CGSizeMake(cellWidth, cellWidth); // constrain photo frame size
+
+ ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space
+ [verticalStack setChildren:@[headerWithInset, _photoImageNode, footerWithInset]];
+
+ return verticalStack;
+}
+
+
+
+
+
+
+
+
+
+Here you can see that the children of the `headerSubStack` depend on whether or not the `_photoLocationLabel` attributed string has returned from the reverseGeocode process yet.
+
+The `_userAvatarImageNode`, `_photoImageNode`, and `_photoCommentsNode` are added into the ASLayoutSpec, but will not show up until their data fetches return.
+
+### Updating an ASLayoutSpec ###
+
+**If something happens that you know will change your `ASLayoutSpec`, it is your job to call `setNeedsLayout`**. This is equivalent to `transitionLayout:duration:0` in the Transition Layout API. You can see this call in the completion block of the `photo.location reverseGeocodedLocationWithCompletionBlock:` call in the first code block.
+
+An appropriately constructed ASLayoutSpec will know which subnodes need to be added, removed or animated.
+
+Try out the ASDKgram sample app after looking at the code above, and you will see how simple it is to code an `ASCellNode` whose layout is responsive to numerous, individual data fetches and returns. While the `ASLayoutSpec` is coded in a way that leaves holes for the avatar and photo to populate, you can see how the cell's height will automatically adjust to accomodate the comments node at the bottom of the photo.
+
+This is just a simple example, but this feature has many more powerful uses.
+
+
+Warning: addSubnode: and removeFromSupernode should never be called on a node that has ASM enabled. Doing so could cause the following exception - "A flattened layout must consist exclusively of node sublayouts".
+
diff --git a/docs/_docs/batch-fetching-api.md b/docs/_docs/batch-fetching-api.md
new file mode 100755
index 00000000..4cb153e2
--- /dev/null
+++ b/docs/_docs/batch-fetching-api.md
@@ -0,0 +1,151 @@
+---
+title: Batch Fetching API
+layout: docs
+permalink: /docs/batch-fetching-api.html
+prevPage: hit-test-slop.html
+nextPage: automatic-subnode-mgmt.html
+---
+
+AsyncDisplayKit's Batch Fetching API makes it easy to add fetching chunks of new data. Usually this would be done in a `-scrollViewDidScroll:` method, but ASDK provides a more structured mechanism.
+
+By default, as a user is scrolling, when they approach the point in the table or collection where they are 2 "screens" away from the end of the current content, the table will try to fetch more data.
+
+If you'd like to configure how far away from the end you should be, just change the `leadingScreensForBatching` property on an `ASTableView` or `ASCollectionView` to something else.
+
+
+
SwiftObjective-C
+
+
+
+tableNode.view.leadingScreensForBatching = 3.0; // overriding default of 2.0
+
+
+tableNode.view.leadingScreensForBatching = 3.0 // overriding default of 2.0
+
+
+
+
+### Batch Fetching Delegate Methods
+
+The first thing you have to do in order to support batch fetching, is implement a method that decides if it's an appropriate time to load new content or not.
+
+For tables it would look something like:
+
+
+
SwiftObjective-C
+
+
+
+- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode
+{
+ if (_weNeedMoreContent) {
+ return YES;
+ }
+
+ return NO;
+}
+
+
+func shouldBatchFetchForTableNode(tableNode: ASTableNode) -> Bool {
+ if (weNeedMoreContent) {
+ return true
+ }
+
+ return false
+}
+
+
+
+
+and for collections:
+
+
+
SwiftObjective-C
+
+
+
+
+- (BOOL)shouldBatchFetchForCollectionNode:(ASCollectionNode *)collectionNode
+{
+ if (_weNeedMoreContent) {
+ return YES;
+ }
+
+ return NO;
+}
+
+
+func shouldBatchFetchForCollectionNode(collectionNode: ASCollectionNode) -> Bool {
+ if (weNeedMoreContent) {
+ return true
+ }
+
+ return false
+}
+
+
+
+
+These methods will be called when the user has scrolled into the batch fetching range, and their answer will determine if another request actually needs to be made or not. Usually this decision is based on if there is still data to fetch.
+
+If you return NO, then no new batch fetching process will happen. If you return YES, the batch fetching mechanism will start and the following method will be called next.
+
+`-tableNode:willBeginBatchFetchWithContext:`
+
+or
+
+`-collectionNode:willBeginBatchFetchWithContext:`
+
+This is where you should actually fetch data, be it from a web API or some local database.
+
+
+Note: This method will always be called on a background thread. This means, if you need to do any work on the main thread, you should dispatch it to the main thread and then proceed with the work needed in order to finish the batch fetch operation.
+
+
+
+
SwiftObjective-C
+
+
+
+- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context
+{
+ // Fetch data most of the time asynchronoulsy from an API or local database
+ NSArray *newPhotos = [SomeSource getNewPhotos];
+
+ // Insert data into table or collection node
+ [self insertNewRowsInTableNode:newPhotos];
+
+ // Decide if it's still necessary to trigger more batch fetches in the future
+ _stillDataToFetch = ...;
+
+ // Properly finish the batch fetch
+ [context completeBatchFetching:YES];
+}
+
+
+func tableNode(tableNode: ASTableNode, willBeginBatchFetchWithContext context: ASBatchContext) {
+ // Fetch data most of the time asynchronoulsy from an API or local database
+ let newPhotos = SomeSource.getNewPhotos()
+
+ // Insert data into table or collection view
+ insertNewRowsInTableNode(newPhotos)
+
+ // Decide if it's still necessary to trigger more batch fetches in the future
+ stillDataToFetch = ...
+
+ // Properly finish the batch fetch
+ context.completeBatchFetching(true)
+}
+
+
+
+
+Once you've finished fetching your data, it is very important to let ASDK know that you have finished the process. To do that, you need to call `-completeBatchFetching:` on the `context` object that was passed in with a parameter value of `YES`. This assures that the whole batch fetching mechanism stays in sync and the next batch fetching cycle can happen. Only by passing `YES` will the context know to attempt another batch update when necessary.
+
+Check out the following sample apps to see the batch fetching API in action:
+
diff --git a/docs/_docs/button-node.md b/docs/_docs/button-node.md
new file mode 100755
index 00000000..2dcbd030
--- /dev/null
+++ b/docs/_docs/button-node.md
@@ -0,0 +1,98 @@
+---
+title: ASButtonNode
+layout: docs
+permalink: /docs/button-node.html
+prevPage: cell-node.html
+nextPage: text-node.html
+---
+
+### Basic Usage
+
+`ASButtonNode` subclasses `ASControlNode` in the same way `UIButton` subclasses `UIControl`. In contrast, being able to layer back the subnodes of every button can significantly lighten main thread impact relative to `UIButton`.
+
+### Control State
+
+If you've used `-setTitle:forControlState:` then you already know how to set up an ASButtonNode. The `ASButtonNode` version adds in a few parameters for conveniently setting attributes.
+
+
+
SwiftObjective-C
+
+
+
+[buttonNode setTitle:@"Button Title Normal" withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal];
+
+
+button.setTitle("Button Title Normal", withFont: nil, withColor: UIColor.blueColor(), forState: .Normal)
+
+
+
+
+If you need even more control, you can also opt to use the attributed string version directly:
+
+
+
SwiftObjective-C
+
+
+
+[self.buttonNode setAttributedTitle:attributedTitle forState:ASControlStateNormal];
+
+
+buttonNode.setAttributedTitle(attributedTitle, for: [])
+
+
+
+
+### Target-Action Pairs
+
+Again, analagous to UIKit, you can add sets of target-action pairs to respond to various events.
+
+
+
SwiftObjective-C
+
+
+
+[buttonNode addTarget:self action:@selector(buttonPressed:) forControlEvents:ASControlNodeEventTouchUpInside];
+
+
+button.addTarget(self, action: #selector(buttonPressed(_:)), forControlEvents: .TouchUpInside)
+
+
+
+
+### Content Alignment
+
+`ASButtonNode` offers both `contentVerticalAlignment` and `contentHorizontalAlignment` properties. This allows you to easily set the alignment of the titleLabel or image you're using for your button.
+
+
+
SwiftObjective-C
+
+
+
+self.buttonNode.contentVerticalAlignment = ASVerticalAlignmentTop;
+self.buttonNode.contentHorizontalAlignment = ASHorizontalAlignmentMiddle;
+
+
+buttonNode.contentVerticalAlignment = .Top
+buttonNode.contentHorizontalAlignment = .Middle
+
+
+
+
+Note: At the moment, this property will not work if you aren't using -layoutSpecThatFits:.
+
+
+### Gotchas
+
+There are a few things that might trip up someone new to the framework.
+
+##### View Hierarchies
+Let's say you want to add an `ASButtonNode` to the view of one of your existing view controllers. The first thing you'll notice is that setting a title for a control state doesn't seem to make your title appear. You can fix this by calling `-measure:` on the button which will cause its title label to be measured and laid out.
+
+The next thing you'll notice is that, if you set titles of various lengths for different control states, the button will dynamically grow and shrink as the title changes. This is because changing the title causes `-setNeedsLayout` to be called on the button. Within a node hierarchy, this makes sense, and will work as expected.
+
+Long story short, use an `ASViewController`.
+
+##### Selected State
+
+If you want your button to change to a "selected" state after being tapped, you'll need to do that manually.
+
diff --git a/docs/_docs/cell-node.md b/docs/_docs/cell-node.md
new file mode 100755
index 00000000..85c626a8
--- /dev/null
+++ b/docs/_docs/cell-node.md
@@ -0,0 +1,142 @@
+---
+title: ASCellNode
+layout: docs
+permalink: /docs/cell-node.html
+prevPage: display-node.html
+nextPage: button-node.html
+---
+
+`ASCellNode`, as you may have guessed, is the cell class of ASDK. Unlike the various cells in UIKit, `ASCellNode` can be used with `ASTableNodes`, `ASCollectionNodes` and `ASPagerNodes`, making it incredibly flexible.
+
+### 3 Ways to Party
+
+There are three ways in which you can implement the cells you'll use in your ASDK app: subclassing `ASCellNode`, initializing with an existing `ASViewController` or using an existing UIView or `CALayer`.
+
+#### Subclassing
+
+Subclassing an `ASCellNode` is pretty much the same as subclassing a regular `ASDisplayNode`.
+
+Most likely, you'll write a few of the following:
+
+- `-init` -- Thread safe initialization.
+- `-layoutSpecThatFits:` -- Return a layout spec that defines the layout of your cell.
+- `-didLoad` -- Called on the main thread. Good place to add gesture recognizers, etc.
+- `-layout` -- Also called on the main thread. Layout is complete after the call to super which means you can do any extra tweaking you need to do.
+
+
+#### Initializing with an `ASViewController`
+
+Say you already have some type of view controller written to display a view in your app. If you want to take that view controller and drop its view in as a cell in one of the scrolling nodes or a pager node its no problem.
+
+For example, say you already have a view controller written that manages an `ASTableNode`. To use that table as a page in an `ASPagerNode` you can use `-initWithViewControllerBlock`.
+
+
+
SwiftObjective-C
+
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+{
+ NSArray *animals = self.allAnimals[index];
+
+ ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^UIViewController * _Nonnull{
+ return [[AnimalTableNodeController alloc] initWithAnimals:animals];
+ } didLoadBlock:nil];
+
+ node.preferredFrameSize = pagerNode.bounds.size;
+
+ return node;
+}
+
+
+func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
+ let animals = allAnimals[index]
+
+ let node = ASCellNode(viewControllerBlock: { () -> UIViewController in
+ return AnimalTableNodeController(animals: animals)
+ }, didLoadBlock: nil)
+
+ node.preferredFrameSize = pagerNode.bounds.size
+
+ return node
+}
+
+
+
+
+And this works for any combo of scrolling container node and `UIViewController` subclass. You want to embed random view controllers in your collection node? Go for it.
+
+
+Notice that you need to set the .style.preferredSize of a node created this way. Normally your nodes will implement -layoutSpecThatFits: but since these don't you'll need give the cell a size.
+
+
+
+#### Initializing with a `UIView` or `CALayer`
+
+Alternatively, if you already have a `UIView` or `CALayer` subclass that you'd like to drop in as cell you can do that instead.
+
+
+
SwiftObjective-C
+
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+{
+ NSArray *animal = self.animals[index];
+
+ ASCellNode *node = [[ASCellNode alloc] initWithViewBlock:^UIView * _Nonnull{
+ return [[SomeAnimalView alloc] initWithAnimal:animal];
+ }];
+
+ node.preferredFrameSize = pagerNode.bounds.size;
+
+ return node;
+}
+
+
+func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
+ let animal = animals[index]
+
+ let node = ASCellNode { () -> UIView in
+ return SomeAnimalView(animal: animal)
+ }
+
+ node.preferredFrameSize = pagerNode.bounds.size
+
+ return node
+}
+
+
+
+
+As you can see, its roughly the same idea. That being said, if you're doing this, you may consider converting the existing `UIView` subclass to be an `ASCellNode` subclass in order to gain the advantage of asynchronous display.
+
+### Never Show Placeholders
+
+Usually, if a cell hasn't finished its display pass before it has reached the screen it will show placeholders until it has completed drawing its content.
+
+If placeholders are unacceptable, you can set an `ASCellNode`'s `neverShowPlaceholders` property to `YES`.
+
+
+
SwiftObjective-C
+
+
+node.neverShowPlaceholders = YES;
+
+
+node.neverShowPlaceholders = true
+
+
+
+
+With this property set to `YES`, the main thread will be blocked until display has completed for the cell. This is more similar to UIKit, and in fact makes AsyncDisplayKit scrolling visually indistinguishable from UIKit's, except being faster.
+
+
+Using this option does not eliminate all of the performance advantages of AsyncDisplayKit. Normally, a cell has been preloading and is almost done when it reaches the screen, so the blocking time is very short. Even if the rangeTuningParameters are set to 0 this option outperforms UIKit. While the main thread is waiting, subnode display executes concurrently.
+
+
+### `UITableViewCell` specific propertys
+
+UITableViewCell has properties like selectionStyle, accessoryType and seperatorInset that many of us use sometimes to give the Cell more detail. For this case ASCellNode has the same (passthrough) properties that can be used.
+
+
+UIKits UITableViewCell contains ASCellNode as a subview. Depending how your ASLayoutSpec is defined it may occure that your Layout overlays the UITableViewCell.accessoryView and therefore not visible. Make sure that your Layout doesn't overlays any UITableViewCell's specific properties.
+
diff --git a/docs/_docs/containers-ascollectionnode.md b/docs/_docs/containers-ascollectionnode.md
new file mode 100755
index 00000000..4cad85a8
--- /dev/null
+++ b/docs/_docs/containers-ascollectionnode.md
@@ -0,0 +1,182 @@
+---
+title: ASCollectionNode
+layout: docs
+permalink: /docs/containers-ascollectionnode.html
+prevPage: containers-astablenode.html
+nextPage: containers-aspagernode.html
+---
+
+`ASCollectionNode` is equivalent to UIKit's `UICollectionView` and can be used in place of any `UICollectionView`.
+
+`ASCollectionNode` replaces `UICollectionView`'s required method
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+
+
+override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
+
+
+
+
+with your choice of **_one_** of the following methods
+
+
+
SwiftObjective-C
+
+
+
+- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath
+
+
+override func collectionNode(collectionNode: ASCollectionNode, nodeForItemAtIndexPath indexPath: NSIndexPath) -> ASCellNode
+
+
+
+
+
+or
+
+
+
+
SwiftObjective-C
+
+
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+
+
+override func collectionNode(collectionNode: ASCollectionNode, nodeBlockForItemAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock
+
+
+
+
+It is recommended that you use the node block version of the method so that your collection node will be able to prepare and display all of its cells concurrently.
+
+As noted in the previous section:
+
+
+ - ASCollectionNodes do not utilize cell resuse.
+ - Using the "nodeBlock" method is preferred.
+ - It is very important that the returned node blocks are thread-safe.
+ - ASCellNodes can be used by ASTableNode, ASCollectionNode and ASPagerNode.
+
+
+### Replacing a UICollectionViewController with an ASViewController
+
+AsyncDisplayKit does not offer an equivalent to UICollectionViewController. Instead, you can use the flexibility of ASViewController to recreate any type of UI...ViewController.
+
+Consider, the following ASViewController subclass.
+
+An ASCollectionNode is assigned to be managed by an `ASViewController` in its `-initWithNode:` designated initializer method, thus making it a sort of ASCollectionNodeController.
+
+
+
SwiftObjective-C
+
+
+- (instancetype)init
+{
+ _flowLayout = [[UICollectionViewFlowLayout alloc] init];
+ _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:_flowLayout];
+
+ self = [super initWithNode:_collectionNode];
+ if (self) {
+ _flowLayout.minimumInteritemSpacing = 1;
+ _flowLayout.minimumLineSpacing = 1;
+ }
+
+ return self;
+}
+
+
+
+init() {
+ flowLayout = UICollectionViewFlowLayout()
+ collectionNode = ASCollectionNode(collectionViewLayout: flowLayout)
+
+ super.init(node: collectionNode)
+
+ flowLayout.minimumInteritemSpacing = 1
+ flowLayout.minimumLineSpacing = 1
+}
+
+
+
+
+This works just as well with any node including as an ASTableNode, ASPagerNode, etc.
+
+### Accessing the ASCollectionView
+If you've used previous versions of ASDK, you'll notice that `ASCollectionView` has been removed in favor of `ASCollectionNode`.
+
+
+`ASCollectionView`, an actual `UICollectionView` subclass, is still used internally by `ASCollectionNode`. While it should not be created directly, it can still be used directly by accessing the `.view` property of an `ASCollectionNode`.
+
+Don't forget that a node's `view` or `layer` property should only be accessed after viewDidLoad or didLoad, respectively, have been called.
+
+
+The `LocationCollectionNodeController` above accesses the `ASCollectionView` directly in `-viewDidLoad`.
+
+
+
SwiftObjective-C
+
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _collectionNode.delegate = self;
+ _collectionNode.dataSource = self;
+ _collectionNode.view.allowsSelection = NO;
+ _collectionNode.view.backgroundColor = [UIColor whiteColor];
+}
+
+
+
+override func viewDidLoad() {
+ super.viewDidLoad()
+
+ collectionNode.delegate = self
+ collectionNode.dataSource = self
+ collectionNode.view.allowsSelection = false
+ collectionNode.view.backgroundColor = UIColor.whiteColor()
+}
+
+
+
+
+### Cell Sizing and Layout
+
+As discussed in the previous section, `ASCollectionNode` and `ASTableNode` do not need to keep track of the height of their `ASCellNode`s.
+
+Right now, cells will grow to fit their constrained size and will be laid out by whatever `UICollectionViewLayout` you provide.
+
+Soon, there will be a method such as `ASTableNode`'s `-constrainedSizeForRow:` but at the moment, if you'd like to constrain the size of a cell used in a collection node, you need to wrap your layoutSpec object in an `ASStaticLayoutSpec` and provide it with a
+
+### Examples
+
+The most detailed example of laying out the cells of an `ASCollectionNode` is the CustomCollectionView app. It includes a Pinterest style cell layout using an `ASCollectionNode` and a custom `UICollectionViewLayout`.
+
+#### More Sample Apps with ASCollectionNodes
+
+
+
+### Interoperability with UICollectionViewCells
+
+`ASCollectionNode` supports using UICollectionViewCells alongside native ASCellNodes.
+
+Note that these UIKit cells will **not** have the performance benefits of `ASCellNodes` (like preloading, async layout, and async drawing), even when mixed within the same `ASCollectionNode`.
+
+However, this interoperability allows developers the flexibility to test out the framework without needing to convert all of their cells at once. Read more here.
\ No newline at end of file
diff --git a/docs/_docs/containers-asnodecontroller.md b/docs/_docs/containers-asnodecontroller.md
new file mode 100755
index 00000000..c1c5c272
--- /dev/null
+++ b/docs/_docs/containers-asnodecontroller.md
@@ -0,0 +1,178 @@
+---
+title: "ASNodeController (Beta)"
+layout: docs
+permalink: /docs/containers-asnodecontroller.html
+prevPage: containers-asviewcontroller.html
+nextPage: containers-astablenode.html
+---
+
+
+To use this feature, you will need to import "ASNodeController+Beta.h"
+
+
+The ASDK team has many exciting ideas for expanding `ASNodeController`. Follow along [here](https://github.com/facebook/AsyncDisplayKit/issues/2964) if you'd like to participate in shaping the future of node controllers.
+
+For now, `ASNodeController` remains a simple, but powerful class.
+
+### Example
+
+The [example project](https://github.com/facebook/AsyncDisplayKit/pull/2945) attached in the initial PR modifies the normal [ASDKgram](https://github.com/facebook/AsyncDisplayKit/tree/master/examples/ASDKgram) project to use an `ASNodeController`.
+This `PhotoCellNodeController` is used to manage the fetching of the comments data for a photo in a photo feed, once the photo enters the preload range. This node controller allows us to separate the preloading logic from where it previously existed in the `PhotoCellNode` "view" class.
+
+To convert ASDKgram to use an `ASNodeController`, we first create a `PhotoCellNodeController` class.
+
+This node controller overrides `ASNodeController`'s' `-loadNode` method to create a `PhotoCellNode` once required. It is not neccessary to call super in this method.
+
+This node controller also observes its node's interface state in order to intelligently preload the photo's comment feed model data when the `PhotoCellNode` enters the preload state (which indicates that the photo cell is likely to scroll onscreen soon).
+
+All of this logic can be removed from where it previously existed in the "view" (our `PhotoCellNode` class), leading to a more concise and MVC-friendly view class.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+@implementation PhotoCellNodeController
+
+- (void)loadNode
+{
+ self.node = [[PhotoCellNode alloc] initWithPhotoObject:self.photoModel];
+}
+
+- (void)didEnterPreloadState
+{
+ [super didEnterPreloadState];
+
+ CommentFeedModel *commentFeedModel = _photoModel.commentFeed;
+ [commentFeedModel refreshFeedWithCompletionBlock:^(NSArray *newComments) {
+ // load comments for photo
+ if (commentFeedModel.numberOfItemsInFeed > 0) {
+ [self.node.photoCommentsNode updateWithCommentFeedModel:commentFeedModel];
+ [self.node setNeedsLayout];
+ }
+ }];
+}
+
+@end
+
+
+
+ // Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
+
+
+
+
+Next, we add a mutable array to the `PhotoFeedNodeController` to store our node controllers and instantiate it in the init method.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+@implementation PhotoFeedNodeController
+{
+ PhotoFeedModel *_photoFeed;
+ ASTableNode *_tableNode;
+ NSMutableArray *_photoCellNodeControllers;
+}
+
+- (instancetype)init
+{
+ _tableNode = [[ASTableNode alloc] init];
+ self = [super initWithNode:_tableNode];
+
+ if (self) {
+ self.navigationItem.title = @"ASDK";
+ [self.navigationController setNavigationBarHidden:YES];
+
+ _tableNode.dataSource = self;
+ _tableNode.delegate = self;
+
+ _photoCellNodeControllers = [NSMutableArray array];
+ }
+
+ return self;
+}
+
+
+
+ // Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
+
+
+
+
+To use this node controller, we modify our table row insertion logic to create a `PhotoCellNodeController` rather than a `PhotoCellNode` directly and add it to our node controller array.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (void)insertNewRowsInTableNode:(NSArray *)newPhotos
+{
+ NSInteger section = 0;
+ NSMutableArray *indexPaths = [NSMutableArray array];
+
+ NSUInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed];
+ for (NSUInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) {
+
+ // create photoCellNodeControllers for the new photos
+ PhotoCellNodeController *cellController = [[PhotoCellNodeController alloc] init];
+ cellController.photoModel = [_photoFeed objectAtIndex:row];
+ [_photoCellNodeControllers addObject:cellController];
+
+ // include this index path in the insert rows call for the table
+ NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section];
+ [indexPaths addObject:path];
+ }
+
+ [_tableNode insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
+}
+
+
+
+ // Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
+
+
+
+
+Don't forget to modify the table data source method to return the node controller rather than the cell node.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ PhotoCellNodeController *cellController = [_photoCellNodeControllers objectAtIndex:indexPath.row];
+ // this will be executed on a background thread - important to make sure it's thread safe
+ ASCellNode *(^ASCellNodeBlock)() = ^ASCellNode *() {
+ PhotoCellNode *cellNode = [cellController node];
+ return cellNode;
+ };
+
+ return ASCellNodeBlock;
+}
+
+
+
+ // Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
+
+
+
+
+
+
diff --git a/docs/_docs/containers-aspagernode.md b/docs/_docs/containers-aspagernode.md
new file mode 100755
index 00000000..918e297f
--- /dev/null
+++ b/docs/_docs/containers-aspagernode.md
@@ -0,0 +1,166 @@
+---
+title: ASPagerNode
+layout: docs
+permalink: /docs/containers-aspagernode.html
+prevPage: containers-ascollectionnode.html
+nextPage: display-node.html
+---
+
+`ASPagerNode` is a subclass of `ASCollectionNode` with a specific `UICollectionViewLayout` used under the hood.
+
+Using it allows you to produce a page style UI similar to what you'd create with UIKit's `UIPageViewController`. `ASPagerNode` currently supports staying on the correct page during rotation. It does _not_ currently support circular scrolling.
+
+The main dataSource methods are:
+
+
+
SwiftObjective-C
+
+
+- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode
+
+
+
+func numberOfPagesInPagerNode(pagerNode: ASPagerNode!) -> Int
+
+
+
+
+and
+
+
+
SwiftObjective-C
+
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+
+
+
+func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode!
+
+
+
+
+or
+
+
+
SwiftObjective-C
+
+
+- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index`
+
+
+
+func pagerNode(pagerNode: ASPagerNode!, nodeBlockAtIndex index: Int) -> ASCellNodeBlock!
+
+
+
+
+These two methods, just as with `ASCollectionNode` and `ASTableNode` need to return either an `ASCellNode` or an `ASCellNodeBlock` - a block that creates an `ASCellNode` and can be run on a background thread.
+
+Note that neither methods should rely on cell reuse (they will be called once per row). Also, unlike UIKit, these methods are not called when the row is just about to display.
+
+While `-pagerNode:nodeAtIndex:` will be called on the main thread, `-pagerNode:nodeBlockAtIndex:` is preferred because it concurrently allocates cell nodes, meaning that the `-init:` method of each of your subnodes will be run in the background. **It is very important that node blocks be thread-safe** as they can be called on the main thread or a background queue.
+
+### Node Block Thread Safety Warning
+
+It is imperative that the data model be accessed outside of the node block. This means that it is highly unlikely that you should need to use the index inside of the block.
+
+In the example below, you can see how the index is used to access the photo model before creating the node block.
+
+
+
SwiftObjective-C
+
+
+- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index
+{
+ PhotoModel *photoModel = _photoFeed[index];
+
+ // this part can be executed on a background thread - it is important to make sure it is thread safe!
+ ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
+ PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhoto:photoModel];
+ return cellNode;
+ };
+
+ return cellNodeBlock;
+}
+
+
+
+func pagerNode(pagerNode: ASPagerNode!, nodeBlockAtIndex index: Int) -> ASCellNodeBlock! {
+ guard photoFeed.count > index else { return nil }
+
+ let photoModel = photoFeed[index]
+ let cellNodeBlock = { () -> ASCellNode in
+ let cellNode = PhotoCellNode(photo: photoModel)
+ return cellNode
+ }
+ return cellNodeBlock
+}
+
+
+
+
+### Using an ASViewController For Optimal Performance
+
+One especially useful pattern is to return an `ASCellNode` that is initialized with an existing `UIViewController` or `ASViewController`. For optimal performance, use an `ASViewController`.
+
+
+
SwiftObjective-C
+
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+{
+ NSArray *animals = self.animals[index];
+
+ ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^{
+ return [[AnimalTableNodeController alloc] initWithAnimals:animals];;
+ } didLoadBlock:nil];
+
+ node.style.preferredSize = pagerNode.bounds.size;
+
+ return node;
+}
+
+
+
+func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
+ guard animals.count > index else { return nil }
+
+ let animal = animals[index]
+ let node = ASCellNode(viewControllerBlock: { () -> UIViewController in
+ return AnimalTableNodeController(animals: animals)
+ }, didLoadBlock: nil)
+
+ node.style.preferredSize = pagerNode.bounds.size
+
+ return node
+}
+
+
+
+
+In this example, you can see that the node is constructed using the `-initWithViewControllerBlock:` method. It is usually necessary to provide a cell created this way with a `style.preferredSize` so that it can be laid out correctly.
+
+### Use ASPagerNode as root node of an ASViewController
+
+#### Log message while popping back in the view controller hierarchy
+If you use an `ASPagerNode` embedded in an `ASViewController` in full screen. If you pop back from the view controller hierarchy you will see some error message in the console.
+
+To resolve the error message set `self.automaticallyAdjustsScrollViewInsets = NO;` in `viewDidLoad` in your `ASViewController` subclass.
+
+#### `navigationBar.translucent` is set to YES
+If you have an `ASPagerNode` embedded in an `ASViewController` in full screen and set the `navigationBar.translucent` to `YES`, you will see an error message while pushing the view controller on the view controller stack.
+
+To resolve the error message add `[self.pagerNode waitUntilAllUpdatesAreCommitted];` within `- (void)viewWillAppear:(BOOL)animated` in your `ASViewController` subclass.
+Unfortunately the disadvantage of this is that the first measurement pass will block the main thread until it finishes.
+
+#### Some more details about the error messages above
+The reason for this error message is that due to the asynchronous nature of AsyncDisplayKit, measurement of nodes will happen on a background thread as UIKit will resize the view of the `ASViewController` on on the main thread. The new layout pass has to wait until the old layout pass finishes with an old layout constrained size. Unfortunately while the measurement pass with the old constrained size is still in progress the `ASPagerFlowLayout` that is backing a `ASPagerNode` will print some errors in the console as it expects sizes for nodes already measured with the new constrained size.
+
+### Sample Apps
+
+Check out the following sample apps to see an `ASPagerNode` in action:
+
diff --git a/docs/_docs/containers-astablenode.md b/docs/_docs/containers-astablenode.md
new file mode 100755
index 00000000..509b58ce
--- /dev/null
+++ b/docs/_docs/containers-astablenode.md
@@ -0,0 +1,229 @@
+---
+title: ASTableNode
+layout: docs
+permalink: /docs/containers-astablenode.html
+prevPage: containers-asnodecontroller.html
+nextPage: containers-ascollectionnode.html
+---
+
+`ASTableNode` is equivalent to UIKit's `UITableView` and can be used in place of any `UITableView`.
+
+`ASTableNode` replaces `UITableView`'s required method
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+
+
+
+override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
+
+
+
+
+with your choice of **_one_** of the following methods
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
+
+
+
+override func tableNode(tableNode: ASTableNode, nodeForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNode
+
+
+
+
+or
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+
+
+
+override func tableNode(tableNode: ASTableNode, nodeBlockForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock
+
+
+
+
+
+
+It is recommended that you use the node block version of these methods so that your collection node will be able to prepare and display all of its cells concurrently. This means that all subnode initialization methods can be run in the background. Make sure to keep 'em thread safe.
+
+
+These two methods, need to return either an `ASCellNode` or an `ASCellNodeBlock`. An `ASCellNodeBlock` is a block that creates a `ASCellNode` which can be run on a background thread. Note that `ASCellNodes` are used by `ASTableNode`, `ASCollectionNode` and `ASPagerNode`.
+
+Note that neither of these methods require a reuse mechanism.
+
+### Replacing UITableViewController with ASViewController
+
+AsyncDisplayKit does not offer an equivalent to `UITableViewController`. Instead, use an `ASViewController` initialized with an `ASTableNode`.
+
+Consider, again, the `ASViewController` subclass - PhotoFeedNodeController - from the `ASDKgram sample app` that uses a table node as its managed node.
+
+An `ASTableNode` is assigned to be managed by an `ASViewController` in its `-initWithNode:` designated initializer method.
+
+
+
SwiftObjective-C
+
+
+- (instancetype)init
+{
+ _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
+ self = [super initWithNode:_tableNode];
+
+ if (self) {
+ _tableNode.dataSource = self;
+ _tableNode.delegate = self;
+ }
+
+ return self;
+}
+
+
+
+func initWithModel(models: Array<Model>) {
+ let tableNode = ASTableNode(style:.Plain)
+
+ super.initWithNode(tableNode)
+
+ self.models = models
+ self.tableNode = tableNode
+ self.tableNode.dataSource = self
+
+ return self
+}
+
+
+
+
+### Node Block Thread Safety Warning
+
+It is very important that node blocks be thread-safe. One aspect of that is ensuring that the data model is accessed _outside_ of the node block. Therefore, it is unlikely that you should need to use the index inside of the block.
+
+Consider the following `-tableNode:nodeBlockForRowAtIndexPath:` method from the `PhotoFeedNodeController.m` file in the ASDKgram sample app.
+
+In the example below, you can see how the index is used to access the photo model before creating the node block.
+
+
+
SwiftObjective-C
+
+
+- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ PhotoModel *photoModel = [_photoFeed objectAtIndex:indexPath.row];
+
+ // this may be executed on a background thread - it is important to make sure it is thread safe
+ ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
+ PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhoto:photoModel];
+ cellNode.delegate = self;
+ return cellNode;
+ };
+
+ return cellNodeBlock;
+}
+
+
+
+func tableNode(tableNode: UITableNode!, nodeBlockForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock! {
+ guard photoFeed.count > indexPath.row else { return nil }
+
+ let photoModel = photoFeed[indexPath.row]
+
+ // this may be executed on a background thread - it is important to make sure it is thread safe
+ let cellNodeBlock = { () -> ASCellNode in
+ let cellNode = PhotoCellNode(photo: photoModel)
+ cellNode.delegate = self;
+ return ASCellNode()
+ }
+
+ return cellNodeBlock
+}
+
+
+
+
+
+### Accessing the ASTableView
+
+If you've used previous versions of ASDK, you'll notice that `ASTableView` has been removed in favor of `ASTableNode`.
+
+
+ASTableView, an actual UITableView subclass, is still used internally by ASTableNode. While it should not be created directly, it can still be used directly by accessing the .view property of an ASTableNode.
+
+Don't forget that a node's view or layer property should only be accessed after -viewDidLoad or -didLoad, respectively, have been called.
+
+
+For example, you may want to set a table's separator style property. This can be done by accessing the table node's view in the `-viewDidLoad:` method as seen in the example below.
+
+
+
SwiftObjective-C
+
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _tableNode.view.allowsSelection = NO;
+ _tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _tableNode.view.leadingScreensForBatching = 3.0; // default is 2.0
+}
+
+
+
+override func viewDidLoad() {
+ super.viewDidLoad()
+
+ tableNode.view.allowsSelection = false
+ tableNode.view.separatorStyle = .None
+ tableNode.view.leadingScreensForBatching = 3.0 // default is 2.0
+}
+
+
+
+
+### Table Row Height
+
+An important thing to notice is that `ASTableNode` does not provide an equivalent to `UITableView`'s `-tableView:heightForRowAtIndexPath:`.
+
+This is because nodes are responsible for determining their own height based on the provided constraints. This means you no longer have to write code to determine this detail at the view controller level.
+
+A node defines its height by way of the layoutSpec returned in the `-layoutSpecThatFits:` method. All nodes given a constrained size are able to calculate their desired size.
+
+
+By default, a ASTableNode provides its cells with a size range constraint where the minimum width is the tableNode's width and a minimum height is 0. The maximum width is also the tableNode's width but the maximum height is FLT_MAX.
+
+This is all to say, a `tableNode`'s cells will always fill the full width of the `tableNode`, but their height is flexible making self-sizing cells something that happens automatically.
+
+
+If you call `-setNeedsLayout` on an `ASCellNode`, it will automatically perform another layout pass and if its overall desired size has changed, the table will be informed and will update itself.
+
+This is different from `UIKit` where normally you would have to call reload row / item. This saves tons of code, check out the ASDKgram sample app to see side by side implementations of an `UITableView` and `ASTableNode` implemented social media feed.
+
+### Sample Apps using ASTableNode
+
diff --git a/docs/_docs/containers-asviewcontroller.md b/docs/_docs/containers-asviewcontroller.md
new file mode 100755
index 00000000..6c4962ae
--- /dev/null
+++ b/docs/_docs/containers-asviewcontroller.md
@@ -0,0 +1,69 @@
+---
+title: ASViewController
+layout: docs
+permalink: /docs/containers-asviewcontroller.html
+prevPage: faq.html
+nextPage: containers-asnodecontroller.html
+---
+
+`ASViewController` is a subclass of `UIViewController` that adds several useful features for hosting `ASDisplayNode` hierarchies.
+
+An `ASViewController` can be used in place of any `UIViewController` - including within a `UINavigationController`, `UITabBarController` and `UISplitViewController` or as a modal view controller.
+
+Benefits of using an `ASViewController`:
+
+- Save Memory. An
ASViewController that goes off screen will automatically reduce the size of the fetch data and display ranges of any of its children. This is key for memory management in large applications.
+ASVisibility Feature. When used in an ASNavigationController or ASTabBarController, these classes know the exact number of user taps it would take to make the view controller visible.
+
+
+More features will be added over time, so it is a good idea to base your view controllers off of this class.
+
+## Usage
+
+A `UIViewController` provides a view of its own. An `ASViewController` is assigned a node to manage in its designated initializer `-initWithNode:`.
+
+Consider the following `ASViewController` subclass, `PhotoFeedNodeController`, from the ASDKgram example project that would like to use a table node as its managed node.
+
+This table node is assigned to the `ASViewController` in its `-initWithNode:` designated initializer method.
+
+
+
SwiftObjective-C
+
+
+- (instancetype)init
+{
+ _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
+ self = [super initWithNode:_tableNode];
+
+ if (self) {
+ _tableNode.dataSource = self;
+ _tableNode.delegate = self;
+ }
+
+ return self;
+}
+
+
+
+func initWithModel(models: Array<Model>) {
+ let tableNode = ASTableNode(style:.Plain)
+
+ super.initWithNode(tableNode)
+
+ self.models = models
+
+ self.tableNode = tableNode
+ self.tableNode.delegate = self
+ self.tableNode.dataSource = self
+
+ return self
+}
+
+
+
+
+
+
+Conversion Tip: If your app already has a complex view controller hierarchy, it is perfectly fine to have all of them subclass ASViewController. That is to say, even if you don't use ASViewController's designated initializer -initiWithNode:, and only use the ASViewController in the manner of a traditional UIViewController, this will give you the additional node support if you choose to adopt it in different areas your application.
+
+
diff --git a/docs/_docs/containers-overview.md b/docs/_docs/containers-overview.md
new file mode 100755
index 00000000..7d55b0c4
--- /dev/null
+++ b/docs/_docs/containers-overview.md
@@ -0,0 +1,52 @@
+---
+title: Node Containers
+layout: docs
+permalink: /docs/containers-overview.html
+prevPage: intelligent-preloading.html
+nextPage: node-overview.html
+---
+
+### Use Nodes in Node Containers
+It is highly recommended that you use AsyncDisplayKit's nodes within a node container. AsyncDisplayKit offers the following node containers.
+
+
+
+ | ASDK Node Container |
+ UIKit Equivalent |
+
+
+ | `ASCollectionNode` |
+ in place of UIKit's `UICollectionView` |
+
+
+ | `ASPagerNode` |
+ in place of UIKit's `UIPageViewController` |
+
+
+ | `ASTableNode` |
+ in place of UIKit's `UITableView` |
+
+
+ | `ASViewController` |
+ in place of UIKit's `UIViewController` |
+
+
+ | `ASNavigationController` |
+ in place of UIKit's `UINavigationController`. Implements the `ASVisibility` protocol. |
+
+
+ | `ASTabBarController` |
+ in place of UIKit's `UITabBarController`. Implements the `ASVisibility` protocol. |
+
+
+
+
+Example code and specific sample projects are highlighted in the documentation for each node container.
+
+
+
+### What do I Gain by Using a Node Container?
+
+A node container automatically manages the intelligent preloading of its nodes. This means that all of the node's layout measurement, data fetching, decoding and rendering will be done asynchronously. Among other conveniences, this is why it is recommended to use nodes within a container node.
+
+Note that while it _is_ possible to use nodes directly (without an AsyncDisplayKit node container), unless you add additional calls, they will only start displaying once they come onscreen (as UIKit does). This can lead to performance degredation and flashing of content.
diff --git a/docs/_docs/control-node.md b/docs/_docs/control-node.md
new file mode 100755
index 00000000..4032af0c
--- /dev/null
+++ b/docs/_docs/control-node.md
@@ -0,0 +1,122 @@
+---
+title: ASControlNode
+layout: docs
+permalink: /docs/control-node.html
+prevPage: map-node.html
+nextPage: scroll-node.html
+---
+
+`ASControlNode` is the ASDK equivalent to `UIControl`. You don't create instances of `ASControlNode` directly. Instead, you can use it as a subclassing point when creating controls of your own. In fact, ASTextNode, ASImageNode, ASVideoNode and ASMapNode are all subclasses of `ASControlNode`.
+
+This fact is especially useful when it comes to image and text nodes. Having the ability to add target-action pairs means that you can use any text or image node as a button without having to rely on creating gesture recognizers, as you would with text in UIKit, or creating extraneous views as you might when using `UIButton`.
+
+### Control State
+
+Like `UIControl`, `ASControlNode` has a state which defines its appearance and ability to support user interactions. Its state can be one of any state defined by `ASControlState`.
+
+
+
SwiftObjective-C
+
+
+typedef NS_OPTIONS(NSUInteger, ASControlState) {
+ ASControlStateNormal = 0,
+ ASControlStateHighlighted = 1 << 0, // used when isHighlighted is set
+ ASControlStateDisabled = 1 << 1,
+ ASControlStateSelected = 1 << 2, // used when isSelected is set
+ ...
+};
+
+
+public struct ASControlState : OptionSet {
+ public static var highlighted: ASControlState { get } // used when ASControlNode isHighlighted is set
+ public static var disabled: ASControlState { get }
+ public static var selected: ASControlState { get } // used when ASControlNode isSelected is set
+ public static var reserved: ASControlState { get } // flags reserved for internal framework use
+}
+
+
+
+
+### Target-Action Mechanism
+
+Also similarly to `UIControl`, `ASControlNode`'s have a set of events defined which you can react to by assigning a target-action pair.
+
+The available actions are:
+
+
SwiftObjective-C
+
+
+typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
+{
+ /** A touch-down event in the control node. */
+ ASControlNodeEventTouchDown = 1 << 0,
+ /** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */
+ ASControlNodeEventTouchDownRepeat = 1 << 1,
+ /** An event where a finger is dragged inside the bounds of the control node. */
+ ASControlNodeEventTouchDragInside = 1 << 2,
+ /** An event where a finger is dragged just outside the bounds of the control. */
+ ASControlNodeEventTouchDragOutside = 1 << 3,
+ /** A touch-up event in the control node where the finger is inside the bounds of the node. */
+ ASControlNodeEventTouchUpInside = 1 << 4,
+ /** A touch-up event in the control node where the finger is outside the bounds of the node. */
+ ASControlNodeEventTouchUpOutside = 1 << 5,
+ /** A system event canceling the current touches for the control node. */
+ ASControlNodeEventTouchCancel = 1 << 6,
+ /** All events, including system events. */
+ ASControlNodeEventAllEvents = 0xFFFFFFFF
+};
+
+
+public struct ASControlNodeEvent : OptionSet {
+ /** A touch-down event in the control node. */
+ public static var touchDown: ASControlNodeEvent { get }
+ /** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */
+ public static var touchDownRepeat: ASControlNodeEvent { get }
+ /** An event where a finger is dragged inside the bounds of the control node. */
+ public static var touchDragInside: ASControlNodeEvent { get }
+ /** An event where a finger is dragged just outside the bounds of the control. */
+ public static var touchDragOutside: ASControlNodeEvent { get }
+ /** A touch-up event in the control node where the finger is inside the bounds of the node. */
+ public static var touchUpInside: ASControlNodeEvent { get }
+ /** A touch-up event in the control node where the finger is outside the bounds of the node. */
+ public static var touchUpOutside: ASControlNodeEvent { get }
+ /** A system event canceling the current touches for the control node. */
+ public static var touchCancel: ASControlNodeEvent { get }
+ /** A system event when the Play/Pause button on the Apple TV remote is pressed. */
+ public static var primaryActionTriggered: ASControlNodeEvent { get }
+ /** All events, including system events. */
+ public static var allEvents: ASControlNodeEvent { get }
+}
+
+
+
+
+Assigning a target and action for these events is done with the same methods as a `UIControl`, namely using `–addTarget:action:forControlEvents:`.
+
+### Hit Test Slop
+
+While all node's have a `hitTestSlop` property, this is usually most useful when dealing with controls. Instead of needing to make your control bigger, or needing to override `-hitTest:withEvent:` you can just assign a `UIEdgeInsets` to your control and its boundaries will be expanded accordingly.
+
+
+
SwiftObjective-C
+
+
+CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2;
+CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2;
+
+_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
+
+
+let horizontalDiff = (bounds.size.width - playButton.bounds.size.width) / 2
+let verticalDiff = (bounds.size.height - playButton.bounds.size.height) / 2
+
+playButton.hitTestSlop = UIEdgeInsets(top: -verticalDiff, left: -horizontalDiff, bottom: -verticalDiff, right: -horizontalDiff)
+
+
+
+
+Remember that, since the property is an inset, you'll need to use negative values in order to expand the size of your tappable region.
+
+### Hit Test Visualization
+
+The hit test visualization tool is an option to enable highlighting of the tappable areas of your nodes. To enable it, include `[ASControlNode setEnableHitTestDebug:YES]` in your app delegate in `-application:didFinishLaunchingWithOptions:`.
diff --git a/docs/_docs/corner-rounding.md b/docs/_docs/corner-rounding.md
new file mode 100755
index 00000000..240b1741
--- /dev/null
+++ b/docs/_docs/corner-rounding.md
@@ -0,0 +1,103 @@
+---
+title: Corner Rounding
+layout: docs
+permalink: /docs/corner-rounding.html
+prevPage: synchronous-concurrency.html
+nextPage: debug-tool-hit-test-visualization.html
+---
+
+When it comes to corner rounding, many developers stick with CALayer's `.cornerRadius` property. Unfortunately, this convenient property greatly taxes performance and should only be used when there is _no_ alternative. This post will cover:
+
+
+
+## CALayer's .cornerRadius is Expensive
+
+Why is `.cornerRadius` so expensive? Use of CALayer's `.cornerRadius` property triggers off-screen rendering to perform the clipping operation on every frame - 60 FPS during scrolling - even if the content in that area isn't changing! This means that the GPU has to switch contexts on every frame, between compositing the overall frame + additional passes for each use of `.cornerRadius`.
+
+Importantly, these costs don't show up in the Time Profiler, because they affect work done by the CoreAnimation Render Server on your app's behalf. This intensive thrash annihilates performance for a lot of devices. On the iPhone 4, 4S, and 5 / 5C (along with comparable iPads / iPods), expect to see notably degraded performance. On the iPhone 5S and newer, even if you can't see the impact directly, it will reduce headroom so that it takes less to cause a frame drop.
+
+## Performant Corner Rounding Strategies
+
+There are only three things to consider when picking a corner rounding strategy:
+
+
+- Is there movement underneath the corner?
+- Is there movement through the corner?
+- Are all 4 corners the same node *and* no other nodes intersect in the corner area?
+
+
+Movement **underneath the corner** is any movement behind the corner. For example, as a rounded-corner collection cell scrolls over a background, the background will move underneath and out from under the corners.
+
+To describe movement **through the corner,** imagine a small rounded-corner scroll view containing a much larger photo. As you zoom and pan the photo inside of the scroll view, the photo will move through the corners of the of the scroll view.
+
+
+
+The above image shows movement underneath the corner highlighted in blue and movement through the corner highlighted in orange.
+
+
+Note: There can be movement inside of the rounded-corner object, without moving through the corner. The following image shows content, highlighted in green, inset from the edge with a margin equal to the size of the corner radius. When the content scrolls, it will not move through the corners.
+
+
+
+
+Using the above method to adjust your design to eliminate one source of corner movement can make the difference between being able to use a fast rounding technique, or resorting to `.cornerRadius.`.
+
+The final consideration is to determine if all four corners cover the same node or if any subnodes interesect the corner area.
+
+
+
+### Precomposited Corners
+
+Precomposited corners refer to corners drawn using bezier paths to clip the content in a CGContext / UIGraphicsContext. In this scenario, the corners become part of the image itself — and are "baked in" to the single CALayer. There are two types of precomposited corners.
+
+The absolute best method is to use **precomposited opaque corners**. This is the most efficient method available, resulting in zero alpha blending (although this is much less critical than avoiding offscreen rendering). Unfortunately, this method is also the least flexible; the background behind the corners will need to be a solid color if the rounded image needs to move around on top of it. It's possible, but tricky to make precomposited corners with a textured or photo background - usually it's best to use precomposited alpha corners instead'.'
+
+The second method involves using bezier paths with **precomposited alpha corners** (`[path clip]`). This method is pretty flexible and should be one of the most frequently used. It does incur the cost of alpha blending across the full size of the content, and including an alpha channel increases memory impact by 25% over opaque precompositing - but these costs are tiny on modern devices, and a different order of magnitude than `.cornerRadius` offscreen rendering.
+
+A key limitation of precomposited corners is that the corners must only touch one node and not intersect with any subnodes. If either of these conditions exist, clip corners must be used.
+
+Note that AsyncDisplayKit nodes have a special optimization of `.cornerRadius` that automatically implements precomposited corners **only when using** `.shouldRasterizeDescendants`. It's important to think carefully before you enable rasterization, so don't use this option without first reading all about the concept.
+
+
+If you're looking for a simple, flat-color rounded rectangle or circle, AsyncDisplayKit offers a variety of conveniences to provide this. See `UIImage+ASConveniences.h` for methods to create flat-colored, rounded-corner resizable images using precomposited corners (both alpha and opaque are supported). These are great for use as placeholders for image nodes or backgrounds for ASButtonNode. More precomposited corner methods will be released with AsyncDisplayKit 2.0 release.
+
+
+### Clip Corner
+
+This strategy involves placing **4 seperate opaque corners that sit on top of the content** that needs corner rounding. This method is flexible and has quite good performance. It has minor CPU overhead of 4 seperate layers, one layer for each corner.
+
+
+
+Clip corners applies to two main types of corner rounding situations:
+
+
+- Rounded corners in situations in which the corners touch more than one node or intersect with any subnodes.
+- Rounded corners on top of a stationary texture or photo background. The photo clip corner method is tricky, but useful!
+
+
+
+Check back soon! Clip corner methods may be released in AsyncDisplayKit 2.0 release.
+
+
+## Is it ever okay to use CALayer's .cornerRadius property?
+
+There are a few, quite rare cases in which it is appropriate to use `.cornerRadius.` These include when there is dynamic content moving _both_ through the inside and underneath the corner. For certain animations, this is impossible to avoid. However, in many cases, it is easy to adjust your design to eliminate one of the sources of movement. One such case was discussed in the section on corner movement.
+
+It is much less bad, and okay as a shortcut, to use `.cornerRadius.` for screens in which nothing moves. However, *any* motion on the screen, even movement that doesn't involve the corners, will cause the `.cornerRadius.` perfromance tax. For example, having a rounded element in the navigation bar with a scrolling view beneath it will cause the impact even if they don't overlap. Animating anything onscreen, even if the user doesn't interact, will as well.' Additionally, any type of screen refresh will incur the cost of corner rounding.
+
+### Rasterization and Layerbacking
+
+Some people have suggested that using CALayer's `.shouldRasterize` can improve the performance of the `.cornerRadius` property. This is not well understood option that is generally perilous. As long as nothing causes it to re-rasterize (no movement, no tap to change color, not on a table view that moves, etc), it is okay to use. Generally we don't encourage this because it is very easy to cause much worse performance. For people who have not great app architecture and insist on using CALayer's `.cornerRadius` (e.g. their app is not very performant), this _can_ make a meaningful difference. However, if you are building your app from the ground up, we highly reccommend that you choose one of the better corner rounding strategies above.
+
+CALayer's `.shouldRasterize` is unrelated to AsyncDisplayKit `node.shouldRasterizeDescendents`. When enabled, `.shouldRasterizeDescendents` will prevent the actual view and layer of the subnode children from being created.
+
+## Corner Rounding Strategy Flowchart
+
+Use this flowchart to select the most performant strategy to round a set of corners.
+
+
diff --git a/docs/_docs/debug-tool-ASRangeController.md b/docs/_docs/debug-tool-ASRangeController.md
new file mode 100755
index 00000000..38fc4802
--- /dev/null
+++ b/docs/_docs/debug-tool-ASRangeController.md
@@ -0,0 +1,42 @@
+---
+title: Range Visualization
+layout: docs
+permalink: /docs/debug-tool-ASRangeController.html
+prevPage: debug-tool-pixel-scaling.html
+nextPage: asvisibility.html
+---
+
+## Visualize ASRangeController tuning parameters (PR #1390)
+
+This debug feature adds a semi-transparent subview in the bottom right hand corner of the sharedApplication keyWindow that visualizes the ASRangeTuningParameters per each ASLayoutRangeType for each visible (on-screen) instance of ASRangeController.
+
+- The instances of ASRangeController are represented as bars
+- As you scroll around within ASTable/CollectionViews you can see the parameters (green = Visible, yellow = Display, and red = FetchData) move relative to each other.
+- White arrows on the L and R sides of the individual RangeController bar views indicate the scrolling direction so that you can determine the leading / trailing tuning parameters (especially useful for vertically-oriented rangeControllers whose leading edge might be unclear within the horizontally-oriented bar view).
+- The white debug label above the RangeController bar displays the RangeController dataSource’s class name to differentiate between nested views.
+- The overlay can be moved with a panning gesture in order to see content under it.
+
+This debug feature is useful for highly optimized ASDK apps that require tuning of any ASRangeController. Or for anyone who is curious about how ASRangeControllers work.
+
+The VerticalWithinHorizontal example app contains an ASPagerNode with embedded ASTableViews. In the screenshot with this feature enabled, you can see the two range controllers - ASTableView and ASCollectionView (ASPagerNode) - in the overlay.
+
+- The white arrows to the right of the rangeController bars indicate that the user is currently scrolling down through the table and right through the ASCollectionView/PagerNode.
+- The ASTableView rangeController bar indicates that the range parameters are tuned to both fetch and decode more data in the downward table direction rather than in the reverse direction (which makes sense as the user is scrolling down).
+- Since it’s less obvious whether or not the user will page to the right or left next, the ASCollectionView is tuned to fetch and decode equal amounts of data in each direction.
+- In the video demo, you can see as the user scrolls between pages, that new ASTableView rangeControllers are created and removed in the overlay view.
+
+
+## Limitations
+
+ - only shows onscreen ASRangeControllers
+ - currently the ratio of red (fetch data), yellow (display) and green (visible) are relative to each other, but not between each bar view. So you cannot compare individual bars to eachother
+
+
+## Usage
+In your `AppDelegate.m` file,
+
+ - import
AsyncDisplayKit+Debug.h
+ - add
[ASRangeController setShouldShowRangeDebugOverlay:YES] at the top of your AppDelegate's didFinishLaunchingWithOptions: method
+
+
+**Make sure to call this method before initializing any component that uses an ASRangeControllers (ASTableView, ASCollectionView).**
diff --git a/docs/_docs/debug-tool-hit-test-visualization.md b/docs/_docs/debug-tool-hit-test-visualization.md
new file mode 100755
index 00000000..2b2f5f2a
--- /dev/null
+++ b/docs/_docs/debug-tool-hit-test-visualization.md
@@ -0,0 +1,34 @@
+---
+title: Hit Test Visualization
+layout: docs
+permalink: /docs/debug-tool-hit-test-visualization.html
+prevPage: corner-rounding.html
+nextPage: debug-tool-pixel-scaling.html
+---
+
+## Visualize ASControlNode Tappable Areas
+
+This debug feature adds a semi-transparent highlight overlay on any ASControlNodes containing a `target:action:` pair or gesture recognizer. The tappable range is defined as the ASControlNode’s frame + its `.hitTestSlop` `UIEdgeInsets`. Hit test slop is a unique feature of `ASControlNode` that allows it to extend its tappable range.
+
+In the screenshot below, you can quickly see that
+
+ - The tappable area for the avatar image overlaps the username’s tappable area. In this case, the user avatar image is on top in the view hierarchy and is capturing some touches that should go to the username.
+ - It would probably make sense to expand the `.hitTestSlop` for the username to allow the user to more easily hit it.
+ - I’ve accidentally set the hitTestSlop’s UIEdgeInsets to be positive instead of negative for the photo likes count label. It’s going to be hard for a user to tap the smaller target.
+
+
+
+
+## Restrictions
+
+A _green_ border on the edge(s) of the highlight overlay indicates that that edge of the tapable area is restricted by one of it's superview's tapable areas. An _orange_ border on the edge(s) of the highlight overlay indicates that that edge of the tapable area is clipped by .clipsToBounds of a parent in its hierarchy.
+
+## Usage
+
+In your `AppDelegate.m` file,
+
+- import
AsyncDisplayKit+Debug.h
+ - add
[ASControlNode setEnableHitTestDebug:YES] at the top of your AppDelegate's didFinishLaunchingWithOptions: method
+
+
+**Make sure to call this method before initializing any ASControlNodes - including ASButtonNodes, ASImageNodes, and ASTextNodes.**
diff --git a/docs/_docs/debug-tool-pixel-scaling.md b/docs/_docs/debug-tool-pixel-scaling.md
new file mode 100755
index 00000000..2ff4e5e3
--- /dev/null
+++ b/docs/_docs/debug-tool-pixel-scaling.md
@@ -0,0 +1,60 @@
+---
+title: Image Scaling
+layout: docs
+permalink: /docs/debug-tool-pixel-scaling.html
+prevPage: debug-tool-hit-test-visualization.html
+nextPage: debug-tool-ASRangeController.html
+---
+
+## Visualize ASImageNode.image’s pixel scaling
+
+This debug feature adds a red text overlay on the bottom right hand corner of an ASImageNode if (and only if) the image’s size in pixels does not match it’s bounds size in pixels, e.g.
+
+
+
SwiftObjective-C
+
+
+
+CGFloat imageSizeInPixels = image.size.width * image.size.height;
+CGFloat boundsSizeInPixels = imageView.bounds.size.width * imageView.bounds.size.height;
+CGFloat scaleFactor = imageSizeInPixels / boundsSizeInPixels;
+
+if (scaleFactor != 1.0) {
+ NSString *scaleString = [NSString stringWithFormat:@"%.2fx", scaleFactor];
+ _debugLabelNode.hidden = NO;
+}
+
+
+let imageSizeInPixels = image.size.width * image.size.height
+let boundsSizeInPixels = imageView.bounds.size.width * imageView.bounds.size.height
+let scaleFactor = imageSizeInPixels / boundsSizeInPixels
+
+if scaleFactor != 1.0 {
+ let scaleString = "\(scaleFactor)"
+ _debugLabelNode.hidden = false
+}
+
+
+
+
+
+This debug feature is useful for quickly determining if you are
+
+
+ - downloading and rendering excessive amounts of image data
+ - upscaling a low quality image
+
+
+In the screenshot below of an app with this debug feature enabled, you can see that the avatar image is unnecessarily large (9x too large) for it’s bounds size and that the center picture is more optimized, but not perfectly so. If you control your own endpoint, make sure to return an optimally sized image.
+
+
+
+## Usage
+
+In your `AppDelegate.m` file,
+
+ - import
AsyncDisplayKit+Debug.h
+ - add
[ASImageNode setShouldShowImageScalingOverlay:YES] at the top of your AppDelegate's didFinishLaunchingWithOptions: method
+
+
+**Make sure to call this method before initializing any ASImageNodes.**
diff --git a/docs/_docs/display-node.md b/docs/_docs/display-node.md
new file mode 100755
index 00000000..482cfa6d
--- /dev/null
+++ b/docs/_docs/display-node.md
@@ -0,0 +1,93 @@
+---
+title: ASDisplayNode
+layout: docs
+permalink: /docs/display-node.html
+prevPage: containers-aspagernode.html
+nextPage: cell-node.html
+---
+
+### Node Basics
+
+`ASDisplayNode` is the main view abstraction over `UIView` and `CALayer`. It initializes and owns a `UIView` in the same way `UIViews` create and own their own backing `CALayers`.
+
+
+
SwiftObjective-C
+
+
+
+ASDisplayNode *node = [[ASDisplayNode alloc] init];
+node.backgroundColor = [UIColor orangeColor];
+node.bounds = CGRectMake(0, 0, 100, 100);
+
+NSLog(@"Underlying view: %@", node.view);
+
+
+
+let node = ASDisplayNode()
+node.backgroundColor = UIColor.orangeColor()
+node.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
+
+print("Underlying view: \(node.view)")
+
+
+
+
+A node has all the same properties as a `UIView`, so using them should feel very familiar to anyone familiar with UIKit.
+
+Properties of both views and layers are forwarded to nodes and can be easily accessed.
+
+
+
SwiftObjective-C
+
+
+
+ASDisplayNode *node = [[ASDisplayNode alloc] init];
+node.clipsToBounds = YES; // not .masksToBounds
+node.borderColor = [UIColor blueColor]; //layer name when there is no UIView equivalent
+
+NSLog(@"Backing layer: %@", node.layer);
+
+
+
+let node = ASDisplayNode()
+node.clipsToBounds = true // not .masksToBounds
+node.borderColor = UIColor.blueColor() //layer name when there is no UIView equivalent
+
+print("Backing layer: \(node.layer)")
+
+
+
+
+As you can see, naming defaults to the `UIView` conventions*** unless there is no `UIView` equivalent. You also have access to your underlying `CALayer` just as you would when dealing with a plain `UIView`.
+
+When used with one of the node containers, a node’s properties will be set on a background thread, and its backing view/layer will be lazily constructed with the cached properties collected by the node. You rarely need to worry about jumping to a background thread as this will be taken care of by the framework, but it's important to know that this is happening under the hood.
+
+### View Wrapping
+
+In some cases, it is desirable to initialize a node and provide a view to be used as the backing view. These views are provided via a block that will return a view so that the actual construction of the view can be saved until later. These nodes’ display step happens synchronously. This is because a node can only be asynchronously displayed when it wraps an `_ASDisplayView` (the internal view subclass), not when it wraps a plain `UIView`.
+
+
+
SwiftObjective-C
+
+
+
+ASDisplayNode *node = [ASDisplayNode alloc] initWithViewBlock:^{
+ SomeView *view = [[SomeView alloc] init];
+ return view;
+}];
+
+
+
+let node = ASDisplayNode(viewBlock: { () -> UIView! in
+ let view = SomeView();
+ return view
+})
+
+
+
+
+Doing this allows you to wrap existing views if that is preferable to converting the `UIView` subclass to an `ASDisplayNode` subclass.
+
+
+
*** The only exception is that nodes use `position` instead of `center` for reasons beyond this intro.
+
diff --git a/docs/_docs/editable-text-node.md b/docs/_docs/editable-text-node.md
new file mode 100755
index 00000000..03429195
--- /dev/null
+++ b/docs/_docs/editable-text-node.md
@@ -0,0 +1,153 @@
+---
+title: ASEditableTextNode
+layout: docs
+permalink: /docs/editable-text-node.html
+prevPage: scroll-node.html
+nextPage: multiplex-image-node.html
+---
+
+`ASEditableTextNode` is available to be used anywhere you'd normally use a `UITextView` or `UITextField`. Under the hood, it uses a specialized `UITextView` as its backing view. You can access and configure this view directly any time after the node has loaded, as long as you do it on the main thread.
+
+It's also important to note that this node does not support layer backing due to the fact that it supports user interaction.
+
+### Basic Usage
+
+Using an editable text node as a text input is easy. If you want it to have text by default, you can assign an attributed string to the `attributedText` property.
+
+
+
SwiftObjective-C
+
+
+
+ASEditableTextNode *editableTextNode = [[ASEditableTextNode alloc] init];
+
+editableTextNode.attributedText = [[NSAttributedString alloc] initWithString:@"Lorem ipsum dolor sit amet."];
+editableTextNode.textContainerInset = UIEdgeInsetsMake(8, 8, 8, 8);
+
+
+
+let editableTextNode = ASEditableTextNode()
+
+editableTextNode.attributedText = NSAttributedString(string: "Lorem ipsum dolor sit amet.")
+editableTextNode.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
+
+
+
+
+### Placeholder Text
+
+If you want to display a text box with a placeholder that disappears after a user starts typing, just assign an attributed string to the `attributedPlaceholderText` property.
+
+
+
SwiftObjective-C
+
+
+
+editableTextNode.attributedPlaceholderText = [[NSAttributedString alloc] initWithString:@"Type something here..."];
+
+
+
+editableTextNode.attributedPlaceholderText = NSAttributedString(string: "Type something here...")
+
+
+
+
+The property `isDisplayingPlaceholder` will initially return `YES`, but will toggle to `NO` any time the `attributedText` property is set to a non-empty string.
+
+### Typing Attributes
+
+To set up the style of the text your user will type into this text field, you can set the `typingAttributes`.
+
+
+
+
SwiftObjective-C
+
+
+
+editableTextNode.typingAttributes = @{NSForegroundColorAttributeName: [UIColor blueColor],
+ NSBackgroundColorAttributeName: [UIColor redColor]};
+
+
+
+editableTextNode.typingAttributes = [NSForegroundColorAttributeName: UIColor.blueColor(),
+ NSBackgroundColorAttributeName: UIColor.redColor()]
+
+
+
+
+
+### ASEditableTextNode Delegate
+
+In order to respond to events associated with an editable text node, you can use any of the following delegate methods:
+
+
+-- Indicates to the delegate that the text node began editing.
+
+
+
SwiftObjective-C
+
+
+- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode;
+
+
+optional public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode)
+
+
+
+
+-- Asks the delegate whether the specified text should be replaced in the editable text node.
+
+
+
SwiftObjective-C
+
+
+- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
+
+
+optional public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
+
+
+
+
+-- Indicates to the delegate that the text node's selection has changed.
+
+
+
SwiftObjective-C
+
+
+- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing;
+
+
+optional public func editableTextNodeDidChangeSelection(_ editableTextNode: ASEditableTextNode, fromSelectedRange: NSRange, toSelectedRange: NSRange, dueToEditing: Bool)
+
+
+
+
+-- Indicates to the delegate that the text node's text was updated.
+
+
+
SwiftObjective-C
+
+
+- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode;
+
+
+optional public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode)
+
+
+
+
+-- Indicates to the delegate that teh text node has finished editing.
+
+
+
SwiftObjective-C
+
+
+- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode;
+
+
+optional public func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode)
+
+
+
+
diff --git a/docs/_docs/faq.md b/docs/_docs/faq.md
new file mode 100755
index 00000000..d188c76e
--- /dev/null
+++ b/docs/_docs/faq.md
@@ -0,0 +1,125 @@
+---
+title: FAQ
+layout: docs
+permalink: /docs/faq.html
+prevPage: subclassing.html
+nextPage: containers-asviewcontroller.html
+---
+
+### Common Developer Mistakes
+
+
+
+### Common Conceptual Misunderstandings
+
+
+
+### Common Questions
+
+
+
+### Accessing the node's view before it is loaded
+
+Node `-init` methods are often called off the main thread, therefore it is imperative that no UIKit objects are accessed. Examples of common errors include accessing the node's view or creating a gesture recognizer. Instead, these operations are ideal to perform in `-didLoad`.
+
+Interacting with UIKit in `-init` can cause crashes and performance problems.
+
+
+### Make sure you access your data source outside the node block
+
+The `indexPath` parameter is only valid _outside_ the node block returned in `nodeBlockForItemAtIndexPath:` or `nodeBlockForRowAtIndexPath:`. Because these blocks are executed on a background thread, the `indexPath` may be invalid by execution time, due to additional changes in the data source.
+
+See an example of how to correctly code a node block in the ASTableNode page. Just as with UIKit, it will cause an exception if Nil is returned from the block for any `ASCellNode`.
+
+
+### Take steps to avoid a retain cycle in viewBlocks
+
+When using `initWithViewBlock:` it is important to prevent a retain cycle by capturing a strong reference to self. The two ways that a cycle can be created are by using any instance variable inside the block or directly referencing self without using a weak pointer.
+
+You can use properties instead of instance variables as long as they are accessed on a weak pointer to self.
+
+Because viewBlocks are always executed on the main thread, it is safe to preform UIKit operations (including gesture recognizer creation and addition).
+
+Although the block is destroyed after the view is created, in the event that the block is never run and the view is never created, then a cycle can persist preventing memory from being released.
+
+
+### ASCellNode Reusability
+
+AsyncDisplayKit does not use cell reuse, for a number of specific reasons, one side effect of this is that it eliminates the large class of bugs associated with cell reuse.
+
+
+### LayoutSpecs Are Regenerated
+
+A node's layoutSpec gets regenerated every time its `layoutThatFits:` method is called.
+
+
+### Layout API Sizing
+
+If you're confused by `ASRelativeDimension`, `ASRelativeSize`, `ASRelativeSizeRange` and `ASSizeRange`, check out our Layout API Sizing guide.
+
+
+### CALayer's .cornerRadius Property Kills Performance
+
+CALayer's' .cornerRadius property is a disastrously expensive property that should only be used when there is no alternative. It is one of the least efficient, most render-intensive properties on CALayer (alongside shadowPath, masking, borders, etc). These properties trigger offscreen rendering to perform the clipping operation on every frame — 60FPS during scrolling! — even if the content in that area isn't changing.
+
+Using `.cornerRadius` will visually degraded performance on iPhone 4, 4S, and 5 / 5C (along with comparable iPads / iPods) and reduce head room and make frame drops more likely on 5S and newer devices.
+
+For a longer discussion and easy alternative corner rounding solutions, please read our comprehensive corner rounding guide.
+
+
+### AsyncDisplayKit does not support UIKit Auto Layout or InterfaceBuilder
+
+UIKit Auto Layout and InterfaceBuilder are not supported by AsyncDisplayKit. It is worth noting that both of these technologies are not permitted in established and disciplined iOS development teams, such as at Facebook, Instagram, and Pinterest.
+
+However, AsyncDisplayKit's Layout API provides a variety of ASLayoutSpec objects that allow implementing automatic layout which is more efficient (multithreaded, off the main thread), easier to debug (can step into the code and see where all values come from, as it is open source), and reusable (you can build composable layouts that can be shared with different parts of the UI).
+
+
+### ASDisplayNode keep alive reference
+
+
+
+
+ASTextNode *title=[[ASTextNode alloc]init];
+title.attributedString=Text;
+[self addSubnode:title];
+
+retain cycles
+(
+"-> _keepalive_node -> ASTextNode ",
+"-> _view -> _ASDisplayView "
+)
+
+
+
+
+
+This retain cycle is intentionally created because the node is in a "live" view hierarchy (it is inside the UIWindow that is onscreen).
+
+To see why this is necessary, consider that Apple also creates this retain cycle between UIView and CALayer. If you create a UIView and add its layer to a super layer, and then release the UIView, it will stay alive even though the CALayer delegate pointing to it is weak.
+
+For the same reason, if the node's view is a descendant of a window, but there is no reference to the node, we keep the node alive with a strong reference from the view.
+
+Good application design should not rely on this behavior, because a strong reference to the node should be maintained by the subnodes array or by an instance variable. However, this condition occasionally occurs, for example when using a UIView animation API. This cycle should never create a leak or even extend the lifecycle of a node any longer than it is absolutely necessary.
+
+
+### UICollectionViewCell Compatibility
+
+ASDK supports using UICollectionViewCells alongside native ASCellNodes.
+
+Note that these UIKit cells will **not** have the performance benefits of `ASCellNodes` (like preloading, async layout, and async drawing), even when mixed within the same `ASCollectionNode`.
+
+However, this interoperability allows developers the flexibility to test out the framework without needing to convert all of their cells at once. Read more here.
diff --git a/docs/_docs/getting-started.md b/docs/_docs/getting-started.md
new file mode 100755
index 00000000..4ed58e75
--- /dev/null
+++ b/docs/_docs/getting-started.md
@@ -0,0 +1,53 @@
+---
+title: Getting Started
+layout: docs
+permalink: /docs/getting-started.html
+nextPage: resources.html
+---
+
+AsyncDisplayKit's basic unit is the `node`. `ASDisplayNode` is an abstraction
+over `UIView`, which in turn is an abstraction over `CALayer`. Unlike views, which
+can only be used on the main thread, nodes are thread-safe: you can
+instantiate and configure entire hierarchies of them in parallel on background
+threads.
+
+To keep its user interface smooth and responsive, your app should render at 60
+frames per second — the gold standard on iOS. This means the main thread
+has one-sixtieth of a second to push each frame. That's 16 milliseconds to
+execute all layout and drawing code! And because of system overhead, your code
+usually has less than ten milliseconds to run before it causes a frame drop.
+
+AsyncDisplayKit lets you move image decoding, text sizing and rendering, and
+other expensive UI operations off the main thread, to keep the main thread available to
+respond to user interaction. AsyncDisplayKit has other tricks up its
+sleeve too... but we'll get to that later.
+
+
+
+If you're used to working with views, you already know how to use nodes. Most methods have a node equivalent and most `UIView` and `CALayer` properties are available as well. In any case where there is a naming discrepancy (such as `.clipsToBounds` vs `.masksToBounds`), nodes will default to the `UIView` name. The only exception is that nodes use position instead of center.
+
+Of course, you can always access the underlying view or layer directly via `node.view` or `node.layer`, just make sure to do it on the main thread!
+
+AsyncDisplayKit offers a variety of nodes to replace the majority of the UIKit components that you are used to. Large scale apps have been able to completely write their UI using just AsyncDisplayKit nodes.
+
+
+
+When converting an app to use AsyncDisplayKit, a common mistake is to add nodes directly to an existing view hierarchy. Doing this will virtually guarantee that your nodes will flash as they are rendered.
+
+Instead, you should add nodes as subnodes of one of the many node container classes. These containers are in charge of telling contained nodes what state they're currently in so that data can be loaded and nodes can be rendered as efficiently as possible. You should think of these classes as the integration point between UIKit and ASDK.
+
+
+
+AsyncDisplayKit's layout engine is both one of its most powerful and one of its most unique features. Based on the CSS FlexBox model, it provides a declarative way of specifying a custom node's size and layout of its subnodes. While all nodes are concurrently rendered by default, asynchronous measurement and layout are performed by providing an `ASLayoutSpec` for each node.
+
+
+
+AsyncDisplayKit offers a variety of advanced developer features that cannot be found in UIKit or Foundation. Our developers have found that AsyncDisplyKit allows simplifications in their architecture and improves developer velocity.
+
+(Full list coming soon!)
+
+
+
+If you are new to AsyncDisplayKit, we recommend that you check out our ASDKgram example app. We've created a handy guide (coming soon!) with step-by-step directions and a follow along example on how to add AsyncDisplayKit to an app.
+
+If you run into any problems along the way, reach out to us GitHub or the AsyncDisplayKit Slack community for help.
diff --git a/docs/_docs/hit-test-slop.md b/docs/_docs/hit-test-slop.md
new file mode 100755
index 00000000..157d7ae5
--- /dev/null
+++ b/docs/_docs/hit-test-slop.md
@@ -0,0 +1,44 @@
+---
+title: Hit Test Slop
+layout: docs
+permalink: /docs/hit-test-slop.html
+prevPage: layout-transition-api.html
+nextPage: batch-fetching-api.html
+---
+
+`ASDisplayNode` has a `hitTestSlop` property of type `UIEdgeInsets` that when set to a non-zero inset, increase the bounds for hit testing to make it easier to tap or perform gestures on this node.
+
+ASDisplayNode is the base class for all nodes, so this property is available on any of AsyncDisplayKit's nodes.
+
+
+Note: This affects the default implementation of -hitTest and -pointInside, so subclasses should call super if you override it and want hitTestSlop applied.
+
+
+A node's ability to capture touch events is restricted by its parent's bounds + parent hitTestSlop UIEdgeInsets. Should you want to extend the hitTestSlop of a child outside its parent's bounds, simply extend the parent node's hitTestSlop to include the child's hitTestSlop needs.
+
+### Usage
+
+A common need for hit test slop, is when you have a text node (aka label) you'd like to use as a button. Often, the text node's height won't meet the 44 point minimum recommended for tappable areas. In that case, you can calculate the difference, and apply a negative inset to your label to increase the tappable area.
+
+
+
SwiftObjective-C
+
+
+
+ASTextNode *textNode = [[ASTextNode alloc] init];
+
+CGFloat padding = (44.0 - button.bounds.size.height)/2.0;
+textNode.hitTestSlop = UIEdgeInsetsMake(-padding, 0, -padding, 0);
+
+
+let textNode = ASTextNode()
+
+let padding = (44.0 - button.bounds.size.height)/2.0
+textNode.hitTestSlop = UIEdgeInsetsMake(-padding, 0, -padding, 0)
+
+
+
+
+
+To visualize
hitTestSlop, check out the
debug tool.
+
diff --git a/docs/_docs/image-modification-block.md b/docs/_docs/image-modification-block.md
new file mode 100755
index 00000000..3a4e1a44
--- /dev/null
+++ b/docs/_docs/image-modification-block.md
@@ -0,0 +1,47 @@
+---
+title: Image Modification Blocks
+layout: docs
+permalink: /docs/image-modification-block.html
+prevPage: inversion.html
+nextPage: placeholder-fade-duration.html
+---
+
+Many times, operations that would affect the appearance of an image you're displaying are big sources of main thread work. Naturally, you want to move these to a background thread.
+
+By assigning an `imageModificationBlock` to your imageNode, you can define a set of transformations that need to happen asynchronously to any image that gets set on the imageNode.
+
+
+
SwiftObjective-C
+
+
+
+_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
+ UIImage *newImage = [image applyBlurWithRadius:30
+ tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
+ saturationDeltaFactor:1.8
+ maskImage:nil];
+ return newImage ? newImage : image;
+};
+
+//some time later...
+
+_backgroundImageNode.image = someImage;
+
+
+
+backgroundImageNode.imageModificationBlock = { image in
+ let newImage = image.applyBlurWithRadius(30, tintColor: UIColor(white: 0.5, alpha: 0.3),
+ saturationDeltaFactor: 1.8,
+ maskImage: nil)
+ return (newImage != nil) ? newImage : image
+}
+
+//some time later...
+
+backgroundImageNode.image = someImage
+
+
+
+
+The image named "someImage" will now be blurred asynchronously before being assigned to the imageNode to be displayed.
+
diff --git a/docs/_docs/image-node.md b/docs/_docs/image-node.md
new file mode 100755
index 00000000..77b2bdd5
--- /dev/null
+++ b/docs/_docs/image-node.md
@@ -0,0 +1,121 @@
+---
+title: ASImageNode
+layout: docs
+permalink: /docs/image-node.html
+prevPage: text-node.html
+nextPage: network-image-node.html
+---
+
+`ASImageNode` is the ASDK equivalent to `UIImageView`. The most basic difference is that images are decoded asynchronously by default. Of course, there are more advanced improvments as well such as GIF support and `imageModificationBlock`s.
+
+### Basic Usage
+
+Using an image node works exactly like using an image view.
+
+
+
SwiftObjective-C
+
+
+
+ASImageNode *imageNode = [[ASImageNode alloc] init];
+
+imageNode.image = [UIImage imageNamed:@"someImage"];
+imageNode.contentMode = UIViewContentModeScaleAspectFill;
+
+
+
+let imageNode = ASImageNode()
+
+imageNode.image = UIImage(named: "someImage")
+imageNode.contentMode = .ScaleAspectFill
+
+
+
+
+
+### Image Modification Block
+
+Many times, operations that would affect the appearance of an image you're displaying are big sources of main thread work. Naturally, you want to move these to a background thread. By assigning an `imageModificationBlock` to your `imageNode`, you can define a set of transformations that need to happen asynchronously to any image that gets set on the `imageNode`.
+
+
+
SwiftObjective-C
+
+
+
+_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
+ UIImage *newImage = [image applyBlurWithRadius:30
+ tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
+ saturationDeltaFactor:1.8
+ maskImage:nil];
+ return newImage ? newImage : image;
+};
+
+//some time later...
+
+_backgroundImageNode.image = someImage;
+
+
+
+backgroundImageNode.imageModificationBlock = { image in
+ let newImage = image.applyBlurWithRadius(30, tintColor: UIColor(white: 0.5, alpha: 0.3),
+ saturationDeltaFactor: 1.8,
+ maskImage: nil)
+ return (newImage != nil) ? newImage : image
+}
+
+//some time later...
+
+backgroundImageNode.image = someImage
+
+
+
+
+The image named "someImage" will now be blurred asynchronously before being assigned to the `imageNode` to be displayed.
+
+### Image Cropping
+
+When an `imageNode`'s `contentMode` property is set to `UIViewContentModeScaleAspectFill`, it will automatically expand the image to fill the entire area of the imageNode, and crop any areas that go past the bounds due to scaling the image.
+
+By default, the expanded image will be centered within the bounds of the view. Take the following cat image. His face gets cut off by default.
+
+
+
+That's messed up. To fix it, you can set the `cropRect` property to move the image over. By default it is set to `CGRectMake(0.5, 0.5, 0.0, 0.0)`.
+
+The rectangle is specified as a "unit rectangle," using percentages of the source image's width and height. To show the image starting at the left side, you can set the `cropRect`'s `x` value to be `0.0`, meaning the image's origin should start at `{0, 0}` as opposed to the default.
+
+
+
SwiftObjective-C
+
+
+
+self.animalImageNode.cropRect = CGRectMake(0, 0, 0.0, 0.0);
+
+
+
+animalImageNode.cropRect = CGRect(x: 0, y: 0, width: 0.0, height: 0.0)
+
+
+
+
+Leaving the width and height values at `0.0` means the image won't be stretched.
+
+
+
+Alternatively, you can set the `x` value of the origin to `1.0` to right align the image.
+
+
+
+### Forced Upscaling
+
+By default, an image won't be upscaled on the CPU when it is too small to fit into the bounds of the `imageNode` it has been set on.
+
+You can set `forceUpscaling` to `YES` if you'd like to change this fact. Doing so means your app will take up more memory any time you use an image that is smaller than its destination.
+
+### Detecting Image Scaling
+
+By using the pixel scaling tool, you can easily check each image in your app to see how much it has been scaled up or down.
+
+If images are too big, you risk rendering excessive amounts of image data, and when they're too small you spend time upscaling a low quality image.
+
+If you control your API, consider returning correctly scaled images so that this work can be avoided.
diff --git a/docs/_docs/index.html b/docs/_docs/index.html
new file mode 100755
index 00000000..930d84dd
--- /dev/null
+++ b/docs/_docs/index.html
@@ -0,0 +1,4 @@
+---
+layout: redirect
+destination: /docs/getting-started.html
+---
diff --git a/docs/_docs/inset-layout-spec.md b/docs/_docs/inset-layout-spec.md
new file mode 100755
index 00000000..ad3bd3e5
--- /dev/null
+++ b/docs/_docs/inset-layout-spec.md
@@ -0,0 +1,7 @@
+---
+title: ASInsetLayoutSpec
+layout: docs
+permalink: /docs/inset-layout-spec.html
+---
+
+😑 This page is coming soon...
\ No newline at end of file
diff --git a/docs/_docs/installation.md b/docs/_docs/installation.md
new file mode 100755
index 00000000..10f39e8b
--- /dev/null
+++ b/docs/_docs/installation.md
@@ -0,0 +1,114 @@
+---
+title: Installation
+layout: docs
+permalink: /docs/installation.html
+prevPage: resources.html
+nextPage: adoption-guide-2-0-beta1.html
+---
+
+AsyncDisplayKit may be added to your project via CocoaPods or Carthage. Do not forget to import the framework header:
+
+
+
+or create a Objective-C bridging header (Swift). If you have any problems installing AsyncDisplayKit, please contact us on Github or Slack!
+
+## CocoaPods
+
+AsyncDisplayKit is available on CocoaPods. Add the following to your Podfile:
+
+
+
+
+target 'MyApp' do
+ pod "AsyncDisplayKit"
+end
+
+
+
+
+Quit Xcode completely before running
+
+
+
+in the project directory in Terminal.
+
+To update your version of AsyncDisplayKit, run
+
+
+
+
+> pod update AsyncDisplayKit
+
+
+
+
+in the project directory in Terminal.
+
+Don't forget to use the workspace `.xcworkspace` file, _not_ the project `.xcodeproj` file.
+
+## Carthage (standard build)
+
+
+The standard way to use Carthage is to have a Cartfile list the dependencies, and then run `carthage update` to download the dependenices into the `Cathage/Checkouts` folder and build each of those into frameworks located in the `Carthage/Build` folder, and finally the developer has to manually integrate in the project.
+
+
+AsyncDisplayKit is also available through Carthage.
+
+Add the following to your Cartfile to get the **latest release** branch:
+
+
+
+
+github "facebook/AsyncDisplayKit"
+
+
+
+
+
+Or, to get the **master** branch:
+
+
+
+
+github "facebook/AsyncDisplayKit" "master"
+
+
+
+
+
+AsyncDisplayKit has its own Cartfile which lists its dependencies, so this is the only line you will need to include in your Cartfile.
+
+Run
+
+
+
+
+in Terminal. This will fetch dependencies into a `Carthage/Checkouts` folder, then build each one.
+
+Look for terminal output confirming `AsyncDisplayKit`, `PINRemoteImage (3.0.0-beta.2)` and `PINCache` are all fetched and built. The ASDK framework Cartfile should handle the dependencies correctly.
+
+In Xcode, on your application targets’ **“General”** settings tab, in the **“Linked Frameworks and Libraries”** section, drag and drop each framework you want to use from the `Carthage/Build` folder on disk.
+
+## Carthage (light)
+
+AsyncDisplayKit does not yet support the lighter way of using Carthage, in which you manually add the project files. This is because one of its dependencies, `PINCache` (a nested dependency of `PINRemoteImage`) does not yet have a project file.
+
+Without including `PINRemoteImage` and `PINCache`, you will not get AsyncDisplayKit's full image feature set.
diff --git a/docs/_docs/intelligent-preloading.md b/docs/_docs/intelligent-preloading.md
new file mode 100755
index 00000000..e5353a59
--- /dev/null
+++ b/docs/_docs/intelligent-preloading.md
@@ -0,0 +1,95 @@
+---
+title: Intelligent Preloading
+layout: docs
+permalink: /docs/intelligent-preloading.html
+prevPage: upgrading.html
+nextPage: containers-overview.html
+---
+
+While a node's ability to be rendered and measured asynchronously and concurrently makes it quite powerful, another crucially important layer to ASDK is the idea of intelligent preloading.
+
+As was pointed out in Getting Started, it is rarely advantageous to use a node outside of the context of one of the node containers. This is due to the fact that all nodes have a notion of their current interface state.
+
+This `interfaceState` property is constantly updated by an `ASRangeController` which all containers create and maintain internally.
+
+A node used outside of a container won't have its state updated by any range controller. This sometimes results in a flash as nodes are rendered after realizing they're already onscreen without any warning.
+
+## Interface State Ranges
+
+When nodes are added to a scrolling or paging interface they are typically in one of the following ranges. This means that as the scrolling view is scrolled, their interface states will be updated as they move through them.
+
+
+
+A node will be in one of following ranges:
+
+
+
+ | Interface State |
+ Description |
+
+
+ | Preload |
+ The furthest range out from being visible. This is where content is gathered from an external source, whether that’s some API or a local disk. |
+
+
+ | Display |
+ Here, display tasks such as text rasterization and image decoding take place. |
+
+
+ | Visible |
+ The node is onscreen by at least one pixel. |
+
+
+
+## ASRangeTuningParameters
+
+The size of each of these ranges is measured in "screenfuls". While the default sizes will work well for many use cases, they can be tweaked quite easily by setting the tuning parameters for range type on your scrolling node.
+
+
+
+In the above visualization of a scrolling collection, the user is scrolling down. As you can see, the sizes of the ranges in the leading direction are quite a bit larger than the content the user is moving away from (the trailing direction). If the user were to change directions, the leading and trailing sides would dynamically swap in order to keep memory usage optimal. This allows you to worry about defining the leading and trailing sizes without having to worry about reacting to the changing scroll directions of your user.
+
+Intelligent preloading also works in multiple dimensions.
+
+## Interface State Callbacks
+
+As a user scrolls, nodes move through the ranges and react appropriately by loading data, rendering, etc. Your own node subclasses can easily tap into this mechanism by implementing the corresponding callback methods.
+
+#### Visible Range
+
+
+
+
+
+-didEnterVisibleState
+-didExitVisibleState
+
+
+
+
+#### Display Range
+
+
+
+
+
+-didEnterDisplayState
+-didExitDisplayState
+
+
+
+
+#### Preload Range
+
+
+
+
+
+-didEnterPreloadState
+-didExitPreloadState
+
+
+
+
+
+Just remember to call super ok? 😉
diff --git a/docs/_docs/inversion.md b/docs/_docs/inversion.md
new file mode 100755
index 00000000..d28edb1e
--- /dev/null
+++ b/docs/_docs/inversion.md
@@ -0,0 +1,32 @@
+---
+title: Inversion
+layout: docs
+permalink: /docs/inversion.html
+prevPage: automatic-subnode-mgmt.html
+nextPage: image-modification-block.html
+---
+
+`ASTableNode` and `ASCollectionNode` have a `inverted` property of type `BOOL` that when set to `YES`, will automatically invert the content so that it's layed out bottom to top, that is the 'first' (indexPath 0, 0) node is at the bottom rather than the top as usual. This is extremely covenient for chat/messaging apps, and with AsyncDisplayKit it only takes one property.
+
+When this is enabled, developers only have to take one more step to have full inversion support and that is to adjust the `contentInset` of their `ASTableNode` or `ASCollectionNode` like so:
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+ CGFloat inset = [self topBarsHeight];
+ self.tableNode.view.contentInset = UIEdgeInsetsMake(0, 0, inset, 0);
+ self.tableNode.view.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, inset, 0);
+
+
+
+
+
+
+
+
+See the SocialAppLayout-Inverted example project for more details.
diff --git a/docs/_docs/layer-backing.md b/docs/_docs/layer-backing.md
new file mode 100755
index 00000000..d02a65f9
--- /dev/null
+++ b/docs/_docs/layer-backing.md
@@ -0,0 +1,29 @@
+---
+title: Layer Backing
+layout: docs
+permalink: /docs/layer-backing.html
+prevPage: accessibility.html
+nextPage: subtree-rasterization.html
+---
+
+In some cases, you can substantially improve your app's performance by using layers instead of views. **We recommend enabling layer-backing in any custom node that doesn't need touch handling**.
+
+With UIKit, manually converting view-based code to layers is laborious due to the difference in APIs. Worse, if at some point you need to enable touch handling or other view-specific functionality, you have to manually convert everything back (and risk regressions!).
+
+With all AsyncDisplayKit nodes, converting an entire subtree from views to layers is as simple as:
+
+
+
SwiftObjective-C
+
+
+rootNode.layerBacked = YES;
+
+
+rootNode.layerBacked = true
+
+
+
+
+...and if you need to go back, it's as simple as deleting one line.
+
+
diff --git a/docs/_docs/layout-api-debugging.md b/docs/_docs/layout-api-debugging.md
new file mode 100755
index 00000000..495098d7
--- /dev/null
+++ b/docs/_docs/layout-api-debugging.md
@@ -0,0 +1,69 @@
+---
+title: Layout Debugging
+layout: docs
+permalink: /docs/layout-api-debugging.html
+prevPage: automatic-layout-containers.html
+nextPage: layout-api-sizing.html
+---
+
+Here are some helpful questions to ask yourself when you encounter any issues composing layoutSpecs.
+
+## Am I the child of an `ASStackLayoutSpec` or an `ASStaticLayoutSpec`?
+
+Certain `ASLayoutable` properties will _only_ apply when the layoutable is a child of a _stack_ spec (the child is called an ASStackLayoutable), while other properties _only_ apply when the layoutable is a child of a _static_ spec (the child is called an ASStaticLayoutable).
+
+- table of [`ASStackLayoutables` properties](http://asyncdisplaykit.org/docs/automatic-layout-containers.html#asstacklayoutable-properties)
+- table of [`ASStaticLayoutable` properties](http://asyncdisplaykit.org/docs/automatic-layout-containers.html#asstaticlayoutable-properties)
+
+All ASLayoutable properties can be applied to _any_ layoutable (e.g. any node or layout spec), however certain properties will only take effect depending on the type of the parent layout spec they are wrapped in.
+
+## Have I considered where to set `ASLayoutable` properties?
+
+Let's say you have a node (`n1`) and you wrap it in a layout spec (`s1`). If you want to wrap the layout spec (`s1`) in a stack or static spec (`s2`), you will need to set all of the properties on the spec (`s1`) and not the node (`n1`).
+
+A common examples of this confusion involves flex grow and flex shrink. E.g. a node with `.flexGrow` enabled is wrapped in an inset spec. The inset spec will not grow as we expect. **Solution:** enable `.flexGrow` on the inset spec as well.
+
+## Have I provided sizes for any node that lacks an intrinsic size?
+
+AsyncDisplayKit's layout pass is recursive, starting at the layout spec returned from `-layoutSpecThatFits:` and proceeding down until it reaches the leaf nodes included in any nested layout specs.
+
+Some leaf nodes have a concept of their own intrinsic size, such as ASTextNode or ASImageNode. A text node knows the length of its formatted string and an ASImageNode knows the size of its static image. Other leaf nodes require an intrinsic size to be set.
+
+Nodes that require the developer to provide an intrinsic size:
+
+- `ASNetworkImageNode` or `ASMultiplexImageNode` have no intrinsic size until the image is downloaded. **A size must be provided for either node.**
+- `ASVideoNode` or `ASVideoNodePlayer` have no intrinsic size until the video is downloaded. **A size must be provided for either node.**
+- `ASDisplayNode` custom subclasses may provide their intrinisc size by implementing `-calculateSizeThatFits:`.
+
+To provide an intrinisc size for these nodes that lack intrinsic sizes (even if only momentarily), you can set one of the following:
+
+- set `.preferredFrameSize` on any node.
+- set `.sizeRange` for children of **static** specs only.
+- implement `-calculateSizeThatFits:` for **custom ASDisplayNode subclasses only**.
+
+*_Note that .preferredFrameSize is not considered by ASTextNodes. Also, setting .sizeRange on a node will override the node's intrinisic size provided by -calculateSizeThatFits:_
+
+## When do I use `.preferredFrameSize` vs `.sizeRange`?
+
+Set `.preferredFrameSize` to set a size for any node. Note that setting .preferredFrameSize on an `ASTextNode` will silently fail. We are working on fixing this, but in the meantime, you can wrap the ASTextNode in a static spec and provide it a .sizeRange.
+
+Set `.sizeRange` to set a size range for any node or layout spec that is the child of a *static* spec consisting. `.sizeRange` is the *only* way to set a size on a layout spec. Again, when using `.sizeRange`, you *must wrap the layoutable in a static layout spec for it to take effect.*
+
+A `.sizeRange` consists of a minimum and maximum constrained size (these sizes can be a specific point value or a relative value, like 70%). For details on the `.sizeRange` property's custom value type, check out our [Layout API Sizing guide](http://asyncdisplaykit.org/docs/layout-api-sizing.html).
+
+## `ASRelativeDimension` vs `ASRelativeSize` vs `ASRelativeSizeRange` vs `ASSizeRange`
+
+AsyncDisplayKit's Layout API supports configuring node and layout spec sizes with specific point values as well as relative values. Read the [Layout API Sizing guide](http://asyncdisplaykit.org/docs/layout-api-sizing.html) for a helpful chart and documentation on our custom layout value types.
+
+## Debugging layout specs with ASCII art
+
+Calling `-asciiArtString` on any `ASDisplayNode` or `ASLayoutSpec` returns an ascii-art representation of the object and its children. An example of a simple layoutSpec ascii-art console output can be seen below.
+
+```
+-----------------------ASStackLayoutSpec----------------------
+| -----ASStackLayoutSpec----- -----ASStackLayoutSpec----- |
+| | ASImageNode | | ASImageNode | |
+| | ASImageNode | | ASImageNode | |
+| --------------------------- --------------------------- |
+--------------------------------------------------------------
+ ```
diff --git a/docs/_docs/layout-api-sizing.md b/docs/_docs/layout-api-sizing.md
new file mode 100755
index 00000000..7937793c
--- /dev/null
+++ b/docs/_docs/layout-api-sizing.md
@@ -0,0 +1,171 @@
+---
+title: Layout API Sizing
+layout: docs
+permalink: /docs/layout-api-sizing.html
+prevPage: layout-api-debugging.html
+nextPage: layout-transition-api.html
+---
+
+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.
+
+
+Note that .flexBasis can be set on any <ASLayoutable> (a node, or a layout spec), but will only take effect if that element is added as a child of a stack layout spec. This container-dependence of layoutable properties is a key area we’re working on clarifying.
+
+
+#### 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).
+
+
+
SwiftObjective-C
+
+
+
+ASRelativeDimensionMakeWithPoints(CGFloat points);
+ASRelativeDimensionMakeWithPercent(CGFloat percent);
+ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value);
+
+
+public func ASDimensionMake(_ points: CGFloat) -> ASDimension
+public func ASDimensionMakeWithFraction(_ fraction: CGFloat) -> ASDimension
+public func ASDimensionMake(_ unit: ASDimensionUnit, _ value: CGFloat)
+
+
+
+
+#### ASRelativeDimension Example
+
+`PIPlaceSingleDetailNode` uses flexBasis to set 2 child nodes of a horizontal stack to share the width 40 / 60:
+
+
+
SwiftObjective-C
+
+
+
+leftSideStack.flexBasis = ASRelativeDimensionMakeWithPercent(0.4f);
+self.detailLabel.flexBasis = ASRelativeDimensionMakeWithPercent(0.6f);
+[horizontalStack setChildren:@[leftSideStack, self.detailLabel]];
+
+
+leftSideStack.style.flexBasis = ASDimensionMake("40%")
+detailsLabel.style.flexBasis = ASDimensionMake("60%")
+horizontalStack.children = [leftSideStack, detailsLabel]
+
+
+
+
+
+
+## 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 each `ASRelativeDimensions`.
+
+- 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.**
+
+
+
SwiftObjective-C
+
+
+
+ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height);
+ASRelativeSizeMakeWithCGSize(CGSize size);
+
+
+ASLayoutSize(width: ASDimension, height: ASDimension)
+// ASRelativeSizeMakeWithCGSize deprecated in swift
+
+
+
+
+## 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`.
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
+
+
+open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+
+
+
+
+#### 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.
+
+
+Note that .sizeRange can be set on any <ASLayoutable> (a node, or a layout spec), but will only take effect if that element is added as a child of a static layout spec. This container-dependence of layoutable properties is a key area we’re working on clarifying.
+
+
+#### 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:
+
+
+
SwiftObjective-C
+
+
+
+ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact);
+
+
+public func ASSizeRangeMake(_ exactSize: CGSize) -> ASSizeRange
+
+
+
+
+### 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.
+
diff --git a/docs/_docs/layout-engine.md b/docs/_docs/layout-engine.md
new file mode 100755
index 00000000..79af87a4
--- /dev/null
+++ b/docs/_docs/layout-engine.md
@@ -0,0 +1,87 @@
+---
+title: Layout Engine
+layout: docs
+permalink: /docs/layout-engine.html
+prevPage: subclassing.html
+nextPage: containers-overview.html
+---
+
+AsyncDisplayKit's layout engine is based on the CSS Box Model. While it is the feature of the framework that bears the weakest resemblance to the UIKit equivalent (AutoLayout), it is also among the most useful features once you've gotten used to it. With enough practice, you may just come to prefer creating declarative layouts to the constraint based approach. ;]
+
+The main way you participate in this system is by implementing `-layoutSpecThatFits:` in a node subclass. Here, you declaratively build up layout specs from the inside out, returning the final spec which will contain the rest.
+
+
+
SwiftObjective-C
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ verticalStack.direction = ASStackLayoutDirectionVertical;
+ verticalStack.spacing = 4.0;
+ [verticalStack setChildren:_commentNodes];
+
+ return verticalStack;
+}
+
+
+
+override func layoutSpecThatFits(constrainedSize: ASSizeRange) {
+ let verticalStack = ASStackLayoutSpec()
+ verticalStack.direction = .Vertical
+ verticalStack.spacing = 4.0
+ verticalStack.setChildren(_commentNodes)
+
+ return verticalStack
+}
+
+
+
+
+Whle this example is extremely simple, it gives you an idea of how to use a layout spec. A stack layout spec, for instance, defines a layout of nodes in which the chlidren will be laid out adjacently, in the direction specified, with the spacing specified. It is very similar to `UIStackView` but with the added benefit of backwards compatibility.
+
+### ASLayoutable
+
+Layout spec's children can be any object whose class conforms to the `` protocol. All nodes, as well as all layout specs conform to the `` protocol. This means that your layout can be built up in composable chunks until you have what you want.
+
+Say you wanted to add 8 pts of padding to the stack you've already set up:
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ verticalStack.direction = ASStackLayoutDirectionVertical;
+ verticalStack.spacing = 4.0;
+ [verticalStack setChildren:_commentNodes];
+
+ UIEdgeInsets insets = UIEdgeInsetsMake(8, 8, 8, 8);
+ ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets
+ child:verticalStack];
+
+ return insetSpec;
+}
+
+
+
+override func layoutSpecThatFits(constrainedSize: ASSizeRange) {
+ let verticalStack = ASStackLayoutSpec()
+ verticalStack.direction = .Vertical
+ verticalStack.spacing = 4.0
+ verticalStack.setChildren(_commentNodes)
+
+ let insets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
+ let insetSpec = ASInsetLayoutSpec(insets: insets, child: verticalStack)
+
+ return insetSpec
+}
+
+
+
+
+You can easily do that by making that stack the child of an inset layout spec.
+
+Naturally, using layout specs takes a bit of practice so to learn more, check out the layout section.
diff --git a/docs/_docs/layout-options.md b/docs/_docs/layout-options.md
new file mode 100755
index 00000000..cdefd2a6
--- /dev/null
+++ b/docs/_docs/layout-options.md
@@ -0,0 +1,52 @@
+---
+title: Layout Options
+layout: docs
+permalink: /docs/layout-options.html
+prevPage: automatic-layout-debugging.html
+nextPage: layer-backing.html
+---
+
+When using ASDK, you have three options for layout. Note that UIKit Autolayout is **not** supported by ASDK.
+#Manual Sizing & Layout
+
+This original layout method shipped with ASDK 1.0 and is analogous to UIKit's layout methods. Use this method for ASViewControllers (unless you subclass the node).
+
+`[ASDisplayNode calculateSizeThatFits:]` **vs.** `[UIView sizeThatFits:]`
+
+`[ASDisplayNode layout]` **vs.** `[UIView layoutSubviews]`
+
+###Advantages (over UIKit)
+- Eliminates all main thread layout cost
+- Results are cached
+
+###Shortcomings (same as UIKit):
+- Code duplication between methods
+- Logic is not reusable
+
+#Unified Sizing & Layout
+
+This layout method does not have a UIKit analog. It is implemented by calling
+
+`- (ASLayout *)calculateLayoutThatFits: (ASSizeRange)constraint`
+
+###Advantages
+- zero duplication
+- still async, still cached
+
+###Shortcomings
+- logic is not reusable, and is still manual
+
+# Automatic, Extensible Layout
+
+This is the reccomended layout method. It does not have a UIKit analog and is implemented by calling
+
+`- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint`
+###Advantages
+- can reuse even complex, custom layouts
+- built-in specs provide automatic layout
+- combine to compose new layouts easily
+- still async, cached, and zero duplication
+
+The diagram below shows how options #2 and #3 above both result in an ASLayout, except that in option #3, the ASLayout is produced automatically by the ASLayoutSpec.
+
+
diff --git a/docs/_docs/layout-transition-api.md b/docs/_docs/layout-transition-api.md
new file mode 100755
index 00000000..0198f088
--- /dev/null
+++ b/docs/_docs/layout-transition-api.md
@@ -0,0 +1,259 @@
+---
+title: Layout Transition API
+layout: docs
+permalink: /docs/layout-transition-api.html
+prevPage: layout2-api-sizing.html
+nextPage: hit-test-slop.html
+---
+
+The Layout Transition API was designed to make all animations with AsyncDisplayKit easy - even transforming an entire set of views into a completely different set of views!
+
+With this system, you simply specify the desired layout and AsyncDisplayKit will do the work to figure out differences from the current layout. It will automatically add new elements, remove unneeded elements after the transiton, and update the position of any existing elements.
+
+There are also easy to use APIs that allow you to fully customize the starting position of newly introduced elements, as well as the ending position of removed elements.
+
+
+
+## Animating between Layouts
+
+The layout Transition API makes it easy to animate between a node's generated layouts in response to some internal state change in a node.
+
+Imagine you wanted to implement this sign up form and animate in the new field when tapping the next button:
+
+
+
+A standard way to implement this would be to create a container node called `SignupNode` that includes two editable text field nodes and a button node as subnodes. We'll include a property on the SignupNode called `fieldState` that will be used to select which editable text field node to show when the node calculates its layout.
+
+The internal layout spec of the `SignupNode` container would look something like this:
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ FieldNode *field;
+ if (self.fieldState == SignupNodeName) {
+ field = self.nameField;
+ } else {
+ field = self.ageField;
+ }
+
+ ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init];
+ [stack setChildren:@[field, self.buttonNode]];
+
+ UIEdgeInsets insets = UIEdgeInsetsMake(15.0, 15.0, 15.0, 15.0);
+ return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:stack];
+}
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ let fieldNode: FieldNode
+
+ if self.fieldState == .signupNodeName {
+ fieldNode = self.nameField
+ } else {
+ fieldNode = self.ageField
+ }
+
+ let stack = ASStackLayoutSpec()
+ stack.children = [fieldNode, buttonNode]
+
+ let insets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
+ return ASInsetLayoutSpec(insets: insets, child: stack)
+}
+
+
+
+
+To trigger a transition from the `nameField` to the `ageField` in this example, we'll update the SignupNode's `.fieldState` property and begin the transition with `transitionLayoutWithAnimation:`.
+
+This method will invalidate the current calculated layout and recompute a new layout with the `ageField` now in the stack.
+
+
+
+ Swift
+ Objective-C
+
+
+
+self.signupNode.fieldState = SignupNodeAge;
+
+[self.signupNode transitionLayoutWithAnimation:YES];
+
+
+self.signupNode.fieldState = .signupNodeName
+self.signupNode.transitionLayout(withAnimation: true, shouldMeasureAsync: true)
+
+
+
+
+In the default implementation of this API, the layout will recalculate the new layout and use its sublayouts to size and position the SignupNode's subnodes without animation. Future versions of this API will likely include a default animation between layouts and we're open to feedback on what you'd like to see here. However, we'll need to implement a custom animation block to handle the signup form case.
+
+The example below represents an override of `animateLayoutTransition:` in the SignupNode.
+
+This method is called after the new layout has been calculated via `transitionLayoutWithAnimation:` and in the implementation we'll perform a specific animation based upon the fieldState property that was set before the animation was triggered.
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
+{
+ if (self.fieldState == SignupNodeName) {
+ CGRect initialNameFrame = [context initialFrameForNode:self.ageField];
+ initialNameFrame.origin.x += initialNameFrame.size.width;
+ self.nameField.frame = initialNameFrame;
+ self.nameField.alpha = 0.0;
+ CGRect finalAgeFrame = [context finalFrameForNode:self.nameField];
+ finalAgeFrame.origin.x -= finalAgeFrame.size.width;
+ [UIView animateWithDuration:0.4 animations:^{
+ self.nameField.frame = [context finalFrameForNode:self.nameField];
+ self.nameField.alpha = 1.0;
+ self.ageField.frame = finalAgeFrame;
+ self.ageField.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ [context completeTransition:finished];
+ }];
+ } else {
+ CGRect initialAgeFrame = [context initialFrameForNode:self.nameField];
+ initialAgeFrame.origin.x += initialAgeFrame.size.width;
+ self.ageField.frame = initialAgeFrame;
+ self.ageField.alpha = 0.0;
+ CGRect finalNameFrame = [context finalFrameForNode:self.ageField];
+ finalNameFrame.origin.x -= finalNameFrame.size.width;
+ [UIView animateWithDuration:0.4 animations:^{
+ self.ageField.frame = [context finalFrameForNode:self.ageField];
+ self.ageField.alpha = 1.0;
+ self.nameField.frame = finalNameFrame;
+ self.nameField.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ [context completeTransition:finished];
+ }];
+ }
+}
+
+
+override func animateLayoutTransition(_ context: ASContextTransitioning) {
+ if fieldState == .signupNodeName {
+ let initialNameFrame = context.initialFrame(for: ageField)
+
+ nameField.frame = initialNameFrame
+ nameField.alpha = 0
+
+ var finalAgeFrame = context.finalFrame(for: nameField)
+ finalAgeFrame.origin.x -= finalAgeFrame.size.width
+
+ UIView.animate(withDuration: 0.4, animations: {
+ self.nameField.frame = context.finalFrame(for: self.nameField)
+ self.nameField.alpha = 1
+ self.ageField.frame = finalAgeFrame
+ self.ageField.alpha = 0
+ }, completion: { finished in
+ context.completeTransition(finished)
+ })
+ } else {
+ var initialAgeFrame = context.initialFrame(for: nameField)
+ initialAgeFrame.origin.x += initialAgeFrame.size.width
+
+ ageField.frame = initialAgeFrame
+ ageField.alpha = 0
+
+ var finalNameFrame = context.finalFrame(for: ageField)
+ finalNameFrame.origin.x -= finalNameFrame.size.width
+
+ UIView.animate(withDuration: 0.4, animations: {
+ self.ageField.frame = context.finalFrame(for: self.ageField)
+ self.ageField.alpha = 1
+ self.nameField.frame = finalNameFrame
+ self.nameField.alpha = 0
+ }, completion: { finished in
+ context.completeTransition(finished)
+ })
+ }
+}
+
+
+
+
+The passed `ASContextTransitioning` context object in this method contains relevant information to help you determine the state of the nodes before and after the transition. It includes getters into old and new constrained sizes, inserted and removed nodes, and even the raw old and new `ASLayout` objects. In the `SignupNode` example, we're using it to determine the frame for each of the fields and animate them in an out of place.
+
+It is imperative to call `completeTransition:` on the context object once your animation has finished, as it will perform the necessary internal steps for the newly calculated layout to become the current `calculatedLayout`.
+
+Note that there hasn't been a use of `addSubnode:` or `removeFromSupernode` during the transition. AsyncDisplayKit's layout transition API analyzes the differences in the node hierarchy between the old and new layout, implicitly performing node insertions and removals via Automatic Subnode Management.
+
+Nodes are inserted before your implementation of `animateLayoutTransition:` is called and this is a good place to manually manage the hierarchy before you begin the animation. Removals are preformed in `didCompleteLayoutTransition:` after you call `completeTransition:` on the context object. If you need to manually perform deletions, override `didCompleteLayoutTransition:` and perform your custom operations. Note that this will override the default behavior and it is recommended to either call `super` or walk through the `removedSubnodes` getter in the context object to perform the cleanup.
+
+Passing `NO` to `transitionLayoutWithAnimation:` will still run through your `animateLayoutTransition:` and `didCompleteLayoutTransition:` implementations with the `[context isAnimated]` property set to `NO`. It is your choice on how to handle this case — if at all. An easy way to provide a default implementation this is to call super:
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
+{
+ if ([context isAnimated]) {
+ // perform animation
+ } else {
+ [super animateLayoutTransition:context];
+ }
+}
+
+
+override func animateLayoutTransition(_ context: ASContextTransitioning) {
+ if context.isAnimated() {
+
+ } else {
+ super.animateLayoutTransition(contet)
+ }
+}
+
+
+
+
+## Animating constrainedSize Changes
+
+There will be times you'll simply want to respond to bounds changes to your node and animate the recalculation of its layout. To handle this case, call `transitionLayoutWithSizeRange:animated:` on your node.
+
+This method is similar to `transitionLayoutWithAnimation:`, but will not trigger an animation if the passed `ASSizeRange` is equal to the current `constrainedSizeForCalculatedLayout` value. This is great for responding to rotation events and view controller size changes:
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
+{
+ [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+ [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
+ [self.node transitionLayoutWithSizeRange:ASSizeRangeMake(size, size) animated:YES];
+ } completion:nil];
+}
+
+
+override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+ super.viewWillTransition(to: size, with: coordinator)
+ coordinator.animate(alongsideTransition: { context in
+ self.node.transitionLayout(with: ASSizeRange(min: size, max: size), animated: true, shouldMeasureAsync: true)
+ })
+}
+
+
+
+
+## Examples that use the Layout Transition API
+
+- [ASDKLayoutTransition](https://github.com/facebook/AsyncDisplayKit/tree/master/examples/ASDKLayoutTransition)
diff --git a/docs/_docs/layout2-api-sizing.md b/docs/_docs/layout2-api-sizing.md
new file mode 100755
index 00000000..1a52caf4
--- /dev/null
+++ b/docs/_docs/layout2-api-sizing.md
@@ -0,0 +1,167 @@
+---
+title: Layout API Sizing
+layout: docs
+permalink: /docs/layout2-api-sizing.html
+nextPage: layout-transition-api.html
+---
+
+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`, `ASDimension`)
+
+`ASDimension` is essentially a **normal CGFloat with support for representing either a point value, a relative percentage value, or an auto value**.
+
+This unit allows the same API to take in both fixed values, as well as relative ones.
+
+
+
+ Swift
+ Objective-C
+
+
+
+// dimension returned is relative (%)
+ASDimensionMake(@"50%");
+ASDimensionMakeWithFraction(0.5);
+
+// dimension returned in points
+ASDimensionMake(@"70pt")
+ASDimensionMake(70);
+ASDimensionMakeWithPoints(70);
+
+
+
+
+
+
+### Example using `ASDimension`
+
+`ASDimension` is used to set the `flexBasis` property on a child of an `ASStackLayoutSpec`. The `flexBasis` property specifies an object's initial size in the stack dimension, where the stack dimension is whether it is a horizontal or vertical stack.
+
+In the following view, we want the left stack to occupy `40%` of the horizontal width and the right stack to occupy `60%` of the width.
+
+
+
+We do this by setting the `.flexBasis` property on the two childen of the horizontal stack:
+
+
+
+ Swift
+ Objective-C
+
+
+
+self.leftStack.style.flexBasis = ASDimensionMake(@"40%");
+self.rightStack.style.flexBasis = ASDimensionMake(@"60%");
+
+[horizontalStack setChildren:@[self.leftStack, self.rightStack]];
+
+
+
+
+
+
+## Sizes (`CGSize`, `ASLayoutSize`)
+
+`ASLayoutSize` is similar to a `CGSize`, but its **width and height values may represent either a point or percent value**. The type of the width and height are independent; either one may be a point or percent value.
+
+
+
+ Swift
+ Objective-C
+
+
+
+ASLayoutSizeMake(ASDimension width, ASDimension height);
+
+
+
+
+
+
+
+`ASLayoutSize` is used for setting a layout element's `.preferredLayoutSize`, `.minLayoutSize` and `.maxLayoutSize` properties. It allows the same API to take in both fixed sizes, as well as relative ones.
+
+
+
+ Swift
+ Objective-C
+
+
+
+// Dimension type "Auto" indicates that the layout element may
+// be resolved in whatever way makes most sense given the circumstances
+ASDimension width = ASDimensionMake(ASDimensionUnitAuto, 0);
+ASDimension height = ASDimensionMake(@"50%");
+
+layoutElement.style.preferredLayoutSize = ASLayoutSizeMake(width, height);
+
+
+
+
+
+
+
+If you do not need relative values, you can set the layout element's `.preferredSize`, `.minSize` and `.maxSize` properties. The properties take regular `CGSize` values.
+
+
+
+ Swift
+ Objective-C
+
+
+
+layoutElement.style.preferredSize = CGSizeMake(30, 160);
+
+
+
+
+
+
+
+Most of the time, you won't want to constrain both width and height. In these cases, you can individually set a layout element's size properties using `ASDimension` values.
+
+
+
+ Swift
+ Objective-C
+
+
+
+layoutElement.style.width = ASDimensionMake(@"50%");
+layoutElement.style.minWidth = ASDimensionMake(@"50%");
+layoutElement.style.maxWidth = ASDimensionMake(@"50%");
+
+layoutElement.style.height = ASDimensionMake(@"50%");
+layoutElement.style.minHeight = ASDimensionMake(@"50%");
+layoutElement.style.maxHeight = ASDimensionMake(@"50%");
+
+
+
+
+
+
+## Size Range (`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**.
+
+`ASSizeRange` is used mostly in the internals of the layout API. However, the `constrainedSize` value passed as an input to `layoutSpecThatFits:` is an `ASSizeRange`.
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
+
+
+
+
+
+
+
+The `constrainedSize` passed to an `ASDisplayNode` subclass' `layoutSpecThatFits:` method is the minimum and maximum sizes that the node should fit in. The minimum and maximum `CGSize`s contained in `constrainedSize` can be used to size the node's layout elements.
diff --git a/docs/_docs/layout2-conversion-guide.md b/docs/_docs/layout2-conversion-guide.md
new file mode 100755
index 00000000..7d120645
--- /dev/null
+++ b/docs/_docs/layout2-conversion-guide.md
@@ -0,0 +1,507 @@
+---
+title: Upgrading to Layout 2.0 (Beta)
+layout: docs
+permalink: /docs/layout2-conversion-guide.html
+---
+
+A list of the changes:
+
+- Introduction of true flex factors
+- `ASStackLayoutSpec` `.alignItems` property default changed to `ASStackLayoutAlignItemsStretch`
+- Rename `ASStaticLayoutSpec` to `ASAbsoluteLayoutSpec`
+- Rename `ASLayoutable` to `ASLayoutElement`
+- Set `ASLayoutElement` properties via `style` property
+- Easier way to size of an `ASLayoutElement`
+- Deprecation of `-[ASDisplayNode preferredFrameSize]`
+- Deprecation of `-[ASLayoutElement measureWithSizeRange:]`
+- Deprecation of `-[ASDisplayNode measure:]`
+- Removal of `-[ASAbsoluteLayoutElement sizeRange]`
+- Rename `ASRelativeDimension` to `ASDimension`
+- Introduction of `ASDimensionUnitAuto`
+
+In addition to the inline examples comparing **1.x** layout code vs **2.0** layout code, the [example projects](https://github.com/facebook/AsyncDisplayKit/tree/master/examples) and layout documentation have been updated to use the new API.
+
+All other **2.0** changes not related to the Layout API are documented here.
+
+## Introduction of true flex factors
+
+With **1.x** the `flexGrow` and `flexShrink` properties were of type `BOOL`.
+
+With **2.0**, these properties are now type `CGFloat` with default values of `0.0`.
+
+This behavior is consistent with the Flexbox implementation for web. See [`flexGrow`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow) and [`flexShrink`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink) for further information.
+
+
+
SwiftObjective-C
+
+
+
+id<ASLayoutElement> layoutElement = ...;
+
+// 1.x:
+layoutElement.flexGrow = YES;
+layoutElement.flexShrink = YES;
+
+// 2.0:
+layoutElement.style.flexGrow = 1.0;
+layoutElement.style.flexShrink = 1.0;
+
+
+
+
+
+
+
+## `ASStackLayoutSpec`'s `.alignItems` property default changed
+
+`ASStackLayoutSpec`'s `.alignItems` property default changed to `ASStackLayoutAlignItemsStretch` instead of `ASStackLayoutAlignItemsStart` to align with the CSS align-items property.
+
+## Rename `ASStaticLayoutSpec` to `ASAbsoluteLayoutSpec` & behavior change
+
+`ASStaticLayoutSpec` has been renamed to `ASAbsoluteLayoutSpec`, to be consistent with web terminology and better represent the intended behavior.
+
+
+
SwiftObjective-C
+
+
+
+// 1.x:
+ASStaticLayoutSpec *layoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[...]];
+
+// 2.0:
+ASAbsoluteLayoutSpec *layoutSpec = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[...]];
+
+
+
+
+
+
+
+**Please note** that there has also been a behavior change introduced. The following text overlay layout was previously created using a `ASStaticLayoutSpec`, `ASInsetLayoutSpec` and `ASOverlayLayoutSpec` as seen in the code below.
+
+
+
+
+Using `INFINITY` for the `top` value in the `UIEdgeInsets` property of the `ASInsetLayoutSpec` allowed the text inset to start at the bottom. This was possible because it would adopt the size of the static layout spec's `_photoNode`.
+
+
+
+ Swift
+ Objective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ _photoNode.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);
+ ASStaticLayoutSpec *backgroundImageStaticSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_photoNode]];
+
+ UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
+ ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_titleNode];
+
+ ASOverlayLayoutSpec *textOverlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:backgroundImageStaticSpec
+ overlay:textInsetSpec];
+
+ return textOverlaySpec;
+}
+
+
+
+
+
+
+
+With the new `ASAbsoluteLayoutSpec` and same code above, the layout would now look like the picture below. The text is still there, but at ~900 pts (offscreen).
+
+
+
+## Rename `ASLayoutable` to `ASLayoutElement`
+
+Remember that an `ASLayoutSpec` contains children that conform to the `ASLayoutElement` protocol. Both `ASDisplayNodes` and `ASLayoutSpecs` conform to this protocol.
+
+The protocol has remained the same as **1.x**, but the name has been changed to be more descriptive.
+
+## Set `ASLayoutElement` properties via `ASLayoutElementStyle`
+
+An `ASLayoutElement`'s properties are are now set via it's `ASLayoutElementStyle` object.
+
+
+
SwiftObjective-C
+
+
+
+id<ASLayoutElement> *layoutElement = ...;
+
+// 1.x:
+layoutElement.spacingBefore = 1.0;
+
+// 2.0:
+layoutElement.style.spacingBefore = 1.0;
+
+
+
+
+
+
+However, the properties specific to an `ASLayoutSpec` are still set directly on the layout spec.
+
+
+
SwiftObjective-C
+
+
+
+// 1.x and 2.0
+ASStackLayoutSpec *stackLayoutSpec = ...;
+stackLayoutSpec.direction = ASStackLayoutDirectionVertical;
+stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentStart;
+
+
+
+
+
+
+## Setting the size of an `ASLayoutElement`
+
+With **2.0** we introduce a new, easier, way to set the size of an `ASLayoutElement`. These methods replace the deprecated `-preferredFrameSize` and `-sizeRange` **1.x** methods.
+
+The following **optional** properties are provided via the layout element's `style` property:
+
+- `-[ASLayoutElementStyle width]`: specifies the width of an ASLayoutElement. The `minWidth` and `maxWidth` properties will override `width`. The height will be set to Auto unless provided.
+
+- `-[ASLayoutElementStyle minWidth]`: specifies the minimum width of an ASLayoutElement. This prevents the used value of the `width` property from becoming smaller than the specified for `minWidth`.
+
+- `-[ASLayoutElementStyle maxWidth]`: specifies the maximum width of an ASLayoutElement. It prevents the used value of the `width` property from becoming larger than the specified for `maxWidth`.
+
+- `-[ASLayoutElementStyle height]`: specifies the height of an ASLayoutElement. The `minHeight` and `maxHeight` properties will override `height`. The width will be set to Auto unless provided.
+
+- `-[ASLayoutElementStyle minHeight]`: specifies the minimum height of an ASLayoutElement. It prevents the used value of the `height` property from becoming smaller than the specified for `minHeight`.
+
+- `-[ASLayoutElementStyle maxHeight]`: specifies the maximum height of an ASLayoutElement. It prevents the used value of the `height` property from becoming larger than the specified for `maxHeight`.
+
+To set both the width and height with a `CGSize` value:
+
+- `-[ASLayoutElementStyle preferredSize]`: Provides a suggested size for a layout element. If the optional minSize or maxSize are provided, and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not provided, the layout element’s size will default to it’s intrinsic content size provided calculateSizeThatFits:
+
+- `-[ASLayoutElementStyle minSize]`: An optional property that provides a minimum size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum size is smaller than its child’s minimum size, the child’s minimum size will be enforced and its size will extend out of the layout spec’s.
+
+- `-[ASLayoutElementStyle maxSize]`: An optional property that provides a maximum size bound for a layout element. If provided, this restriction will always be enforced. If a child layout element’s maximum size is smaller than its parent, the child’s maximum size will be enforced and its size will extend out of the layout spec’s.
+
+To set both the width and height with a relative (%) value (an `ASRelativeSize`):
+
+- `-[ASLayoutElementStyle preferredRelativeSize]`: Provides a suggested RELATIVE size for a layout element. An ASRelativeSize uses percentages rather than points to specify layout. E.g. width should be 50% of the parent’s width. If the optional minRelativeSize or maxRelativeSize are provided, and the preferredRelativeSize exceeds these, the minRelativeSize or maxRelativeSize will be enforced. If this optional value is not provided, the layout element’s size will default to its intrinsic content size provided calculateSizeThatFits:
+
+- `-[ASLayoutElementStyle minRelativeSize]`: An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum relative size is smaller than its child’s minimum relative size, the child’s minimum relative size will be enforced and its size will extend out of the layout spec’s.
+
+- `-[ASLayoutElementStyle maxRelativeSize]`: An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s maximum relative size is smaller than its child’s maximum relative size, the child’s maximum relative size will be enforced and its size will extend out of the layout spec’s.
+
+For example, if you want to set a `width` of an `ASDisplayNode`:
+
+
+
SwiftObjective-C
+
+
+
+// 1.x:
+// no good way to set an intrinsic size
+
+// 2.0:
+ASDisplayNode *ASDisplayNode = ...;
+
+// width 100 points, height: auto
+displayNode.style.width = ASDimensionMakeWithPoints(100);
+
+// width 50%, height: auto
+displayNode.style.width = ASDimensionMakeWithFraction(0.5);
+
+ASLayoutSpec *layoutSpec = ...;
+
+// width 100 points, height 100 points
+layoutSpec.style.preferredSize = CGSizeMake(100, 100);
+
+
+
+
+
+
+If you previously wrapped an `ASLayoutElement` with an `ASStaticLayoutSpec` just to give it a specific size (without setting the `layoutPosition` property on the element too), you don't have to do that anymore.
+
+
+
SwiftObjective-C
+
+
+
+ASStackLayoutSpec *stackLayoutSpec = ...;
+id<ASLayoutElement> *layoutElement = ...;
+
+// 1.x:
+layoutElement.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[layoutElement]];
+stackLayoutSpec.children = @[staticLayoutSpec];
+
+// 2.0:
+layoutElement.style.preferredSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+stackLayoutSpec.children = @[layoutElement];
+
+
+
+
+
+
+If you previously wrapped a `ASLayoutElement` within a `ASStaticLayoutSpec` just to return any layout spec from within `layoutSpecThatFits:` there is a new layout spec now that is called `ASWrapperLayoutSpec`. `ASWrapperLayoutSpec` is an `ASLayoutSpec` subclass that can wrap a `ASLayoutElement` and calculates the layout of the child based on the size given to the `ASLayoutElement`:
+
+
+
SwiftObjective-C
+
+
+
+// 1.x - ASStaticLayoutSpec used as a "wrapper" to return subnode from layoutSpecThatFits:
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[subnode]];
+}
+
+// 2.0
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ return [ASWrapperLayoutSpec wrapperWithLayoutElement:subnode];
+}
+
+// 1.x - ASStaticLayoutSpec used to set size (but not position) of subnode
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ASDisplayNode *subnode = ...;
+ subnode.preferredSize = ...;
+ return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[subnode]];
+}
+
+// 2.0
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ASDisplayNode *subnode = ...;
+ subnode.style.preferredSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height / 2.0);
+ return [ASWrapperLayoutSpec wrapperWithLayoutElement:subnode];
+}
+
+
+
+
+
+
+## Deprecation of `-[ASDisplayNode preferredFrameSize]`
+
+With the introduction of new sizing properties there is no need anymore for the `-[ASDisplayNode preferredFrameSize]` property. Therefore it is deprecated in **2.0**. Instead, use the size values on the `style` object of an `ASDisplayNode`:
+
+
+
SwiftObjective-C
+
+
+
+ASDisplayNode *ASDisplayNode = ...;
+
+// 1.x:
+displayNode.preferredFrameSize = CGSize(100, 100);
+
+// 2.0
+displayNode.style.preferredSize = CGSize(100, 100);
+
+
+
+
+
+
+`-[ASDisplayNode preferredFrameSize]` was not supported properly and was often more confusing than helpful. The new sizing methods should be easier and more clear to implment.
+
+## Deprecation of `-[ASLayoutElement measureWithSizeRange:]`
+
+`-[ASLayoutElement measureWithSizeRange:]` is deprecated in **2.0**.
+
+#### Calling `measureWithSizeRange:`
+
+If you previously called `-[ASLayoutElement measureWithSizeRange:]` to receive an `ASLayout`, call `-[ASLayoutElement layoutThatFits:]` now instead.
+
+
+
SwiftObjective-C
+
+
+
+// 1.x:
+ASLayout *layout = [layoutElement measureWithSizeRange:someSizeRange];
+
+// 2.0:
+ASLayout *layout = [layoutElement layoutThatFits:someSizeRange];
+
+
+
+
+
+
+#### Implementing `measureWithSizeRange:`
+
+If you are implementing a custom `class` that conforms to `ASLayoutElement` (e.g. creating a custom `ASLayoutSpec`) , replace `-measureWithSizeRange:` with `-calculateLayoutThatFits:`
+
+
+
SwiftObjective-C
+
+
+
+// 1.x:
+- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize {}
+
+// 2.0:
+- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize {}
+
+
+
+
+
+
+`-calculateLayoutThatFits:` takes an `ASSizeRange` that specifies a min size and a max size of type `CGSize`. Choose any size in the given range, to calculate the children's size and position and return a `ASLayout` structure with the layout of child components.
+
+Besides `-calculateLayoutThatFits:` there are two additional methods on `ASLayoutElement` that you should know about if you are implementing classes that conform to `ASLayoutElement`:
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
+ restrictedToSize:(ASLayoutElementSize)size
+ relativeToParentSize:(CGSize)parentSize;
+
+
+
+
+
+
+In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the `layoutElement`'s size, parent size, and constrained size. With these values you could calculate the final constrained size and call `-calculateLayoutThatFits:` with the result.
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
+ parentSize:(CGSize)parentSize;
+
+
+
+
+
+
+Call this on children`layoutElements` to compute their layouts within your implementation of `-calculateLayoutThatFits:`.
+
+For sample implementations of layout specs and the usage of the `calculateLayoutThatFits:` family of methods, check out the layout specs in AsyncDisplayKit itself!
+
+## Deprecation of `-[ASDisplayNode measure:]`
+
+Use `-[ASDisplayNode layoutThatFits:]` instead to get an `ASLayout` and call `size` on the returned `ASLayout`:
+
+
+
SwiftObjective-C
+
+
+
+// 1.x:
+CGSize size = [displayNode measure:CGSizeMake(100, 100)];
+
+// 2.0:
+ASLayout *layout = [displayNode layoutThatFits:ASSizeMake(CGSizeZero, CGSizeMake(100, 100))];
+CGSize size = layout.size;
+
+
+
+
+
+
+## Remove of `-[ASAbsoluteLayoutElement sizeRange]`
+
+The `sizeRange` property was removed from the `ASAbsoluteLayoutElement` protocol. Instead set the one of the following:
+
+- `-[ASLayoutElement width]`
+- `-[ASLayoutElement height]`
+- `-[ASLayoutElement minWidth]`
+- `-[ASLayoutElement minHeight]`
+- `-[ASLayoutElement maxWidth]`
+- `-[ASLayoutElement maxHeight]`
+
+
+
SwiftObjective-C
+
+
+
+id<ASLayoutElement> layoutElement = ...;
+
+// 1.x:
+layoutElement.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+
+// 2.0:
+layoutElement.style.preferredSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+
+
+
+
+
+
+Due to the removal of `-[ASAbsoluteLayoutElement sizeRange]`, we also removed the `ASRelativeSizeRange`, as the type was no longer needed.
+
+## Rename `ASRelativeDimension` to `ASDimension`
+
+To simplify the naming and support the fact that dimensions are widely used in ASDK now, `ASRelativeDimension` was renamed to `ASDimension`. Having a shorter name and handy functions to create it was an important goal for us.
+
+`ASRelativeDimensionTypePercent` and associated functions were renamed to use `Fraction` to be consistent with Apple terminology.
+
+
+
SwiftObjective-C
+
+
+
+// 2.0:
+// Handy functions to create ASDimensions
+ASDimension dimensionInPoints;
+dimensionInPoints = ASDimensionMake(ASDimensionTypePoints, 5.0)
+dimensionInPoints = ASDimensionMake(5.0)
+dimensionInPoints = ASDimensionMakeWithPoints(5.0)
+dimensionInPoints = ASDimensionMake("5.0pt");
+
+ASDimension dimensionInFractions;
+dimensionInFractions = ASDimensionMake(ASDimensionTypeFraction, 0.5)
+dimensionInFractions = ASDimensionMakeWithFraction(0.5)
+dimensionInFractions = ASDimensionMake("50%");
+
+
+
+
+
+
+## Introduction of `ASDimensionUnitAuto`
+
+Previously `ASDimensionUnitPoints` and `ASDimensionUnitFraction` were the only two `ASDimensionUnit` enum values available. A new dimension type called `ASDimensionUnitAuto` now exists. All of the ``ASLayoutElementStyle` sizing properties are set to `ASDimensionAuto` by default.
+
+`ASDimensionUnitAuto` means more or less: *"I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.*
+
+Most of the time this is the intrinsic content size of the `ASLayoutElement`.
+
+For example, if an `ASImageNode` has a `width` set to `ASDimensionUnitAuto`, the width of the linked image file will be used. For an `ASTextNode` the intrinsic content size will be calculated based on the text content. If an `ASLayoutElement` cannot provide any intrinsic content size like `ASVideoNode` for example the size needs to set explicitly.
+
+
+
SwiftObjective-C
+
+
+
+// 2.0:
+// No specific size needs to be set as the imageNode's size
+// will be calculated from the content (the image in this case)
+ASImageNode *imageNode = [ASImageNode new];
+imageNode.image = ...;
+
+// Specific size must be set for ASLayoutElement objects that
+// do not have an intrinsic content size (ASVideoNode does not
+// have a size until it's video downloads)
+ASVideoNode *videoNode = [ASVideoNode new];
+videoNode.style.preferredSize = CGSizeMake(200, 100);
+
+
+
+
+
+
diff --git a/docs/_docs/layout2-layout-element-properties.md b/docs/_docs/layout2-layout-element-properties.md
new file mode 100755
index 00000000..2efb71be
--- /dev/null
+++ b/docs/_docs/layout2-layout-element-properties.md
@@ -0,0 +1,146 @@
+---
+title: Layout Element Properties
+layout: docs
+permalink: /docs/layout2-layout-element-properties.html
+prevPage: layout2-layoutspec-types.html
+nextPage: layout2-api-sizing.html
+---
+
+- ASStackLayoutElement Properties - will only take effect on a node or layout spec that is the child of a stack spec
+- ASAbsoluteLayoutElement Properties - will only take effect on a node or layout spec that is the child of a absolute spec
+- ASLayoutElement Properties - applies to all nodes & layout specs
+
+## ASStackLayoutElement Properties
+
+
+
Please note that the following properties will only take effect if set on the child of an STACK layout spec.
+
+
+
+
+ | Property |
+ Description |
+
+
+ | `CGFloat .style.spacingBefore` |
+ Additional space to place before this object in the stacking direction. |
+
+
+ | `CGFloat .style.spacingAfter` |
+ Additional space to place after this object in the stacking direction. |
+
+
+ | `BOOL .style.flexGrow` |
+ If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? |
+
+
+ | `BOOL .style.flexShrink` |
+ If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? |
+
+
+ | `ASDimension .style.flexBasis` |
+ Specifies the initial size for this object, in the stack dimension (horizontal or vertical), before the `flexGrow` / `flexShrink` properties are applied and the remaining space is distributed. |
+
+
+ | `ASStackLayoutAlignSelf .style.alignSelf` |
+ Orientation of the object along cross axis, overriding alignItems. Options include:
+
+ - `ASStackLayoutAlignSelfAuto`
+ - `ASStackLayoutAlignSelfStart`
+ - `ASStackLayoutAlignSelfEnd`
+ - `ASStackLayoutAlignSelfCenter`
+ - `ASStackLayoutAlignSelfStretch`
+ |
+
+
+ | `CGFloat .style.ascender` |
+ Used for baseline alignment. The distance from the top of the object to its baseline. |
+
+
+ | `CGFloat .style.descender` |
+ Used for baseline alignment. The distance from the baseline of the object to its bottom. |
+
+
+
+
+## ASAbsoluteLayoutElement Properties
+
+
+
Please note that the following properties will only take effect if set on the child of an ABSOLUTE layout spec.
+
+
+
+
+ | Property |
+ Description |
+
+
+ | `CGPoint .style.layoutPosition` |
+ The `CGPoint` position of this object within its `ASAbsoluteLayoutSpec` parent spec. |
+
+
+
+## ASLayoutElement Properties
+
+
+Please note that the following properties apply to ALL layout elements.
+
+
+
+
+ | Property |
+ Description |
+
+
+ | `ASDimension .style.width` |
+ The `width` property specifies the width of the content area of an `ASLayoutElement`. The `minWidth` and `maxWidth` properties override `width`. Defaults to `ASDimensionAuto`. |
+
+
+ | `ASDimension .style.height` |
+ The `height` property specifies the height of the content area of an `ASLayoutElement`. The `minHeight` and `maxHeight` properties override `height`. Defaults to `ASDimensionAuto`. |
+
+
+ | `ASDimension .style.minWidth` |
+ The `minWidth` property is used to set the minimum width of a given element. It prevents the used value of the `width` property from becoming smaller than the value specified for `minWidth`. The value of `minWidth` overrides both `maxWidth` and `width`. Defaults to `ASDimensionAuto`. |
+
+
+ | `ASDimension .style.maxWidth` |
+ The `maxWidth` property is used to set the maximum width of a given element. It prevents the used value of the `width` property from becoming larger than the value specified for `maxWidth`. The value of `maxWidth` overrides `width`, but `minWidth` overrides `maxWidth`. Defaults to `ASDimensionAuto`. |
+
+
+ | `ASDimension .style.minHeight` |
+ The `minHeight` property is used to set the minimum height of a given element. It prevents the used value of the `height` property from becoming smaller than the value specified for `minHeight`. The value of `minHeight` overrides both `maxHeight` and `height`. Defaults to `ASDimensionAuto`. |
+
+
+ | `ASDimension .style.maxHeight` |
+ The `maxHeight` property is used to set the maximum height of a given element. It prevents the used value of the `height` property from becoming larger than the value specified for `maxHeight`. The value of `maxHeight` overrides `height`, but `minHeight` overrides `maxHeight`. Defaults to `ASDimensionAuto` |
+
+
+ | `CGSize .style.preferredSize` |
+ Provides a suggested size for a layout element. If the optional minSize or maxSize are provided, and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not provided, the layout element’s size will default to it’s intrinsic content size provided calculateSizeThatFits:
+ This method is optional, but one of either preferredSize or preferredLayoutSize is required for nodes that either have no intrinsic content size or should be laid out at a different size than its intrinsic content size. For example, this property could be set on an ASImageNode to display at a size different from the underlying image size.
+ Warning: calling the getter when the size's width or height are relative will cause an assert. |
+
+
+ | `CGSize .style.minSize` |
+ An optional property that provides a minimum size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum size is smaller than its child’s minimum size, the child’s minimum size will be enforced and its size will extend out of the layout spec’s.
+ For example, if you set a preferred relative width of 50% and a minimum width of 200 points on an element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, since 160 pts is lower than the minimum width of 200 pts, the minimum width would be used. |
+
+
+ | `CGSize .style.maxSize` |
+ An optional property that provides a maximum size bound for a layout element. If provided, this restriction will always be enforced. If a child layout element’s maximum size is smaller than its parent, the child’s maximum size will be enforced and its size will extend out of the layout spec’s.
+ For example, if you set a preferred relative width of 50% and a maximum width of 120 points on an element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, since 160 pts is higher than the maximum width of 120 pts, the maximum width would be used. |
+
+
+ | `ASLayoutSize .style.preferredLayoutSize` |
+ Provides a suggested RELATIVE size for a layout element. An ASLayoutSize uses percentages rather than points to specify layout. E.g. width should be 50% of the parent’s width. If the optional minLayoutSize or maxLayoutSize are provided, and the preferredLayoutSize exceeds these, the minLayoutSize or maxLayoutSize will be enforced. If this optional value is not provided, the layout element’s size will default to its intrinsic content size provided `calculateSizeThatFits:` |
+
+
+ | `ASLayoutSize .style.minLayoutSize` |
+ An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum relative size is smaller than its child’s minimum relative size, the child’s minimum relative size will be enforced and its size will extend out of the layout spec’s. |
+
+
+ | `ASLayoutSize .style.maxLayoutSize` |
+ An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s maximum relative size is smaller than its child’s maximum relative size, the child’s maximum relative size will be enforced and its size will extend out of the layout spec’s. |
+
+
diff --git a/docs/_docs/layout2-layoutSpecThatFits.md b/docs/_docs/layout2-layoutSpecThatFits.md
new file mode 100755
index 00000000..cd3e3097
--- /dev/null
+++ b/docs/_docs/layout2-layoutSpecThatFits.md
@@ -0,0 +1,120 @@
+---
+title: Composing Layout Specs
+layout: docs
+permalink: /docs/layout2-layoutSpecThatFits.html
+---
+
+The composing of layout specs and layoutables are happening within the `layoutSpecThatFits:` method. This is where you will put the majority of your layout code. It defines the layout and does the heavy calculation on a background thread.
+
+Every `ASDisplayNode` that would like to layout it's subnodes should should do this by implementing the `layoutSpecThatFits:` method. This method is where you build out a layout spec object that will produce the size of the node, as well as the size and position of all subnodes.
+
+The following `layoutSpecThatFits:` implementation is from the Kittens example and will implement an easy stack layout with an image with a constrained size on the left and a text to the right. The great thing is, by using a `ASStackLayoutSpec` the height is dynamically calculated based on the image height and the height of the text.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ // Set an intrinsic size for the image node
+ CGSize imageSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize)
+ : CGSizeMake(kImageSize, kImageSize);
+ [_imageNode setSizeFromCGSize:imageSize];
+
+ // Shrink the text node in case the image + text gonna be too wide
+ _textNode.flexShrink = YES;
+
+ // Configure stack
+ ASStackLayoutSpec *stackLayoutSpec =
+ [ASStackLayoutSpec
+ stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+ spacing:kInnerPadding
+ justifyContent:ASStackLayoutJustifyContentStart
+ alignItems:ASStackLayoutAlignItemsStart
+ children:_swappedTextAndImage ? @[_textNode, _imageNode] : @[_imageNode, _textNode]];
+
+ // Add inset
+ return [ASInsetLayoutSpec
+ insetLayoutSpecWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding)
+ child:stackLayoutSpec];
+}
+
+
+
+
+
+
+
+
+The result looks like the following:
+
+
+Let's look at some more advanced composition of layout spec and layoutable implementation from the `ASDKGram` example that should give you a feel how layout specs and layoutables can be combined to compose a difficult layout. You can also find this code in the `examples/ASDKGram` folder.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+
+
+
+override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
+
+ // ASImageNode layoutable - constrain avatar image frame size
+ self.userAvatarImageView.width = ASDimensionMake(USER_IMAGE_HEIGHT);
+ self.userAvatarImageView.height = ASDimensionMake(USER_IMAGE_HEIGHT);
+
+ // ASLayoutSpec as spacer
+ let spacer = ASLayoutSpec()
+ spacer.flexGrow = true
+
+ // header stack
+ let headerStack = ASStackLayoutSpec.horizontalStackLayoutSpec()
+ headerStack.alignItems = .Center; // center items vertically in horizontal stack
+ headerStack.justifyContent = .Start; // justify content to left side of header stack
+ headerStack.spacing = HORIZONTAL_BUFFER;
+ headerStack.children = [self.userAvatarImageView, self.userNameLabel, spacer, self.photoTimeIntervalSincePostLabel]
+
+ // header inset stack
+ let insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+ let headerWithInset = ASInsetLayoutSpec(insets: insets, child: headerStack)
+ headerWithInset.flexShrink = true;
+
+ // footer stack
+ let footerStack = ASStackLayoutSpec.verticalStackLayoutSpec();
+ footerStack.spacing = VERTICAL_BUFFER;
+ footerStack.children = self.photoLikesLabel, self.photoDescriptionLabel, self.photoCommentsView
+
+ // footer inset stack
+ let footerInsets = UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER);
+ let footerWithInset = ASInsetLayoutSpec(insets: footerInsets, child: footerStack)
+
+ // ASNetworkImageNode layoutable - constrain photo frame size
+ let cellWidth = constrainedSize.max.width;
+ self.photoImageView.size.width = ASDimensionMake(cellWidth);
+ self.photoImageView.size.height = ASDimensionMake(cellWidth);
+
+ // vertical stack
+ let verticalStack = ASStackLayoutSpec.verticalStackLayoutSpec();
+ verticalStack.alignItems = .Stretch; // stretch headerStack to fill horizontal space
+ verticalStack.children = [headerWithInset, self.photoImageView, footerWithInset]
+ return verticalStack;
+}
+
+
+
+
+After the layout pass happened the result will look like the following:
+
+
+The layout spec object that you create in `layoutSpecThatFits:` is mutable up until the point that it is return in this method. After this point, it will be immutable. It's important to remember not to cache layout specs for use later but instead to recreate them when necessary.
+
+Note: Because it is run on a background thread, you should not set any node.view or node.layer properties here. Also, unless you know what you are doing, do not create any nodes in this method. Additionally, it is not necessary to begin this method with a call to super, unlike other method overrides.
\ No newline at end of file
diff --git a/docs/_docs/layout2-layoutspec-types-examples.md b/docs/_docs/layout2-layoutspec-types-examples.md
new file mode 100755
index 00000000..6b851879
--- /dev/null
+++ b/docs/_docs/layout2-layoutspec-types-examples.md
@@ -0,0 +1,26 @@
+---
+title: Layout Spec Composition Examples
+layout: docs
+permalink: /docs/layout2-layoutspec-types-examples.html
+---
+
+## Text Overlaid on an Image
+
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ...
+ UIEdgeInsets *insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+ ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec alloc] initWithInsets:insets child:textNode];
+ ...
+}
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/_docs/layout2-layoutspec-types.md b/docs/_docs/layout2-layoutspec-types.md
new file mode 100755
index 00000000..3e3281c1
--- /dev/null
+++ b/docs/_docs/layout2-layoutspec-types.md
@@ -0,0 +1,473 @@
+---
+title: Layout Specs
+layout: docs
+permalink: /docs/layout2-layoutspec-types.html
+prevPage: automatic-layout-examples-2.html
+nextPage: layout2-layout-element-properties.html
+---
+
+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];
+}
+
+
+
+// return a single subnode from layoutSpecThatFits:
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ return ASWrapperLayoutSpec(layoutElement: _subnode)
+}
+
+// set a size (but not position)
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ _subnode.style.preferredSize = CGSize(width: constrainedSize.max.width,
+ height: constrainedSize.max.height / 2.0)
+ return ASWrapperLayoutSpec(layoutElement: _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 `` 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`. The amount of space between each child.
+- `alignItems`. Orientation of children along cross axis.
+- `baselineRelativeArrangement`. If `YES` the vertical spacing between two views is measured from the last baseline of the top view to the top of the bottom view.
+
+
+
+ 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;
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ let mainStack = ASStackLayoutSpec(direction: .horizontal,
+ spacing: 6.0,
+ justifyContent: .start,
+ alignItems: .center,
+ children: [titleNode, subtitleNode])
+
+ // 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];
+ ...
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ ...
+ let insets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
+ let headerWithInset = ASInsetLayoutSpec(insets: 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];
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
+ let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
+ return ASOverlayLayoutSpec(child: 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];
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
+ let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
+
+ return ASBackgroundLayoutSpec(child: backgroundNode, 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.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+ ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
+ return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
+ sizingOptions:ASRelativeLayoutSpecSizingOptionDefault
+ child:subnode]
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ let subnode = ASDisplayNodeWithBackgroundColor(UIColor.green, CGSize(width: 60.0, height: 100.0))
+ let centerSpec = ASCenterLayoutSpec(centeringOptions: .XY, sizingOptions: [], child: subnode)
+ return centerSpec
+}
+
+
+
+
+## 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];
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ // Half Ratio
+ let subnode = ASDisplayNodeWithBackgroundColor(UIColor.green, CGSize(width: 100, height: 100.0))
+ let ratioSpec = ASRatioLayoutSpec(ratio: 0.5, child: subnode)
+ return ratioSpec
+}
+
+
+
+
+## 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];
+ ...
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ ...
+ let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
+ let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red, CGSize(width: 70.0, height: 100.0))
+
+ let relativeSpec = ASRelativeLayoutSpec(horizontalPosition: .start,
+ verticalPosition: .start,
+ sizingOption: [],
+ child: foregroundNode)
+
+ let backgroundSpec = ASBackgroundLayoutSpec(child: 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 old `ASStaticLayoutSpec`.
+
+
+
+ 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]];
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ let maxConstrainedSize = constrainedSize.max
+
+ // Layout all nodes absolute in a static layout spec
+ guitarVideoNode.style.layoutPosition = CGPoint.zero
+ guitarVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width, height: maxConstrainedSize.height / 3.0)
+
+ nicCageVideoNode.style.layoutPosition = CGPoint(x: maxConstrainedSize.width / 2.0, y: maxConstrainedSize.height / 3.0)
+ nicCageVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)
+
+ simonVideoNode.style.layoutPosition = CGPoint(x: 0.0, y: maxConstrainedSize.height - (maxConstrainedSize.height / 3.0))
+ simonVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)
+
+ hlsVideoNode.style.layoutPosition = CGPoint(x: 0.0, y: maxConstrainedSize.height / 3.0)
+ hlsVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)
+
+ return ASAbsoluteLayoutSpec(children: [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];
+ ...
+}
+
+
+
+override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
+{
+ ...
+ let spacer = ASLayoutSpec()
+ spacer.style.flexGrow = 1.0
+
+ stack.children = [imageNode, spacer, textNode]
+ ...
+}
+
+
+
diff --git a/docs/_docs/layout2-manual-layout.md b/docs/_docs/layout2-manual-layout.md
new file mode 100755
index 00000000..2d4a16ce
--- /dev/null
+++ b/docs/_docs/layout2-manual-layout.md
@@ -0,0 +1,174 @@
+---
+title: Manual Layout
+layout: docs
+permalink: /docs/layout2-manual-layout.html
+---
+
+## Manual Layout
+After diving in to the automatic way for layout in ASDK there is still the _old_ way to layout manually available. For the sake of completness here is a short description how to accomplish that within ASDK.
+
+### Manual Layout UIKit
+
+Sizing and layout of custom view hierarchies are typically done all at once on the main thread. For example, a custom UIView that minimally encloses a text view and an image view might look like this:
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (CGSize)sizeThatFits:(CGSize)size
+{
+ // size the image
+ CGSize imageSize = [_imageView sizeThatFits:size];
+
+ // size the text view
+ CGSize maxTextSize = CGSizeMake(size.width - imageSize.width, size.height);
+ CGSize textSize = [_textView sizeThatFits:maxTextSize];
+
+ // make sure everything fits
+ CGFloat minHeight = MAX(imageSize.height, textSize.height);
+ return CGSizeMake(size.width, minHeight);
+}
+
+- (void)layoutSubviews
+{
+ CGSize size = self.bounds.size; // convenience
+
+ // size and layout the image
+ CGSize imageSize = [_imageView sizeThatFits:size];
+ _imageView.frame = CGRectMake(size.width - imageSize.width, 0.0f,
+ imageSize.width, imageSize.height);
+
+ // size and layout the text view
+ CGSize maxTextSize = CGSizeMake(size.width - imageSize.width, size.height);
+ CGSize textSize = [_textView sizeThatFits:maxTextSize];
+ _textView.frame = (CGRect){ CGPointZero, textSize };
+}
+
+
+
+
+
+
+
+This isn't ideal. We're sizing our subviews twice — once to figure out how big our view needs to be and once when laying it out — and while our layout arithmetic is cheap and quick, we're also blocking the main thread on expensive text sizing.
+
+We could improve the situation by manually cacheing our subviews' sizes, but that solution comes with its own set of problems. Just adding `_imageSize` and `_textSize` ivars wouldn't be enough: for example, if the text were to change, we'd need to recompute its size. The boilerplate would quickly become untenable.
+
+Further, even with a cache, we'll still be blocking the main thread on sizing *sometimes*. We could try to shift sizing to a background thread with `dispatch_async()`, but even if our own code is thread-safe, UIView methods are documented to [only work on the main thread](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html):
+
+> Manipulations to your application’s user interface must occur on the main
+> thread. Thus, you should always call the methods of the UIView class from
+> code running in the main thread of your application. The only time this may
+> not be strictly necessary is when creating the view object itself but all
+> other manipulations should occur on the main thread.
+
+This is a pretty deep rabbit hole. We could attempt to work around the fact that UILabels and UITextViews cannot safely be sized on background threads by manually creating a TextKit stack and sizing the text ourselves... but that's a laborious duplication of work. Further, if UITextView's layout behaviour changes in an iOS update, our sizing code will break. (And did we mention that TextKit isn't thread-safe either?)
+
+### Manual Layout ASDK
+
+Manual layout within ASDK are realized within two methods:
+
+#### `calculateSizeThatFits` and `layout`
+
+Within `calculateSizeThatFits:` you should provide a intrinsic content size for the node based on the given `constrainedSize`. This method is called on a background thread so perform expensive sizing operations within it.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- [ASDisplayNode calculateSizeThatFits:]
+
+
+
+
+
+
+
+After measurement and layout pass happens further layout can be done in `layout`. This method is called on the main thread. In there, layout operations can be done for nodes that are not playing within the automatic layout system and are referenced within `layoutSpecThatFits:`.
+
+
+
+#### Example
+Our custom node looks like this:
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+#import
+
+...
+
+// perform expensive sizing operations on a background thread
+- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
+{
+ // size the image
+ CGSize imageSize = [_imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
+
+ // size the text node
+ CGSize maxTextSize = CGSizeMake(constrainedSize.width - imageSize.width,
+ constrainedSize.height);
+
+ CGSize textSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, maxTextSize)].size;
+
+ // make sure everything fits
+ CGFloat minHeight = MAX(imageSize.height, textSize.height);
+ return CGSizeMake(constrainedSize.width, minHeight);
+}
+
+// do as little work as possible in main-thread layout
+- (void)layout
+{
+ // layout the image using its cached size
+ CGSize imageSize = _imageNode.calculatedSize;
+ _imageNode.frame = CGRectMake(self.bounds.size.width - imageSize.width, 0.0f,
+ imageSize.width, imageSize.height);
+
+ // layout the text view using its cached size
+ CGSize textSize = _textNode.calculatedSize;
+ _textNode.frame = (CGRect){ CGPointZero, textSize };
+}
+
+
+
+
+
+
+
+`ASImageNode` and `ASTextNode`, like the rest of AsyncDisplayKit, are thread-safe, so we can size them on background threads. The `-layoutThatFits:` method is like `-sizeThatFits:`, but with side effects: it caches the (`calculatedSize`) for quick access later on — like in our now-snappy `-layout` implementation.
+
+As you can see, node hierarchies are sized and laid out in much the same way as their view counterparts. Manually layed out nodes do need to be written with a few things in mind:
+
+* Nodes must recursively measure all of their subnodes in their `-calculateSizeThatFits:` implementations. Note that the `-layoutThatFits:` machinery will only call `-calculateSizeThatFits:` if a new measurement pass is needed (e.g., if the constrained size has changed) and `layoutSpecThatFits:` is *not* implemented.
+
+* Nodes should perform any other expensive pre-layout calculations in `-calculateSizeThatFits:`, caching useful intermediate results in ivars as appropriate.
+
+* Nodes should call `[self invalidateCalculatedSize]` when necessary. For example, `ASTextNode` invalidates its calculated size when its `attributedString` property is changed.
+
+As already mentioned, automatic layout is preferred over manual layout and should be the way to go in most cases.
\ No newline at end of file
diff --git a/docs/_docs/layout2-quickstart.md b/docs/_docs/layout2-quickstart.md
new file mode 100755
index 00000000..11f6a1f1
--- /dev/null
+++ b/docs/_docs/layout2-quickstart.md
@@ -0,0 +1,100 @@
+---
+title: Quickstart
+layout: docs
+permalink: /docs/layout2-quickstart.html
+prevPage: multiplex-image-node.html
+nextPage: automatic-layout-examples-2.html
+---
+
+## Motivation & Benefits
+
+The Layout API was created as a performant alternative to UIKit's Auto Layout, which becomes exponentially expensive for complicated view hierarchies. AsyncDisplayKit's Layout API has many benefits over using UIKit's Auto Layout:
+
+- **Fast**: As fast as manual layout code and significantly faster than Auto Layout
+- **Asynchronous & Concurrent:** Layouts can be computed on background threads so user interactions are not interrupted.
+- **Declarative**: Layouts are declared with immutable data structures. This makes layout code easier to develop, document, code review, test, debug, profile, and maintain.
+- **Cacheable**: Layout results are immutable data structures so they can be precomputed in the background and cached to increase user perceived performance.
+- **Extensible**: Easy to share code between classes.
+
+## Inspired by CSS Flexbox
+
+Those who are familiar with Flexbox will notice many similarities in the two systems. However, AsyncDisplayKit's Layout API does not re-implement all of CSS.
+
+## Basic Concepts
+
+AsyncDisplayKit's layout system is centered around two basic concepts:
+
+1. Layout Specs
+2. Layout Elements
+
+
+### Layout Specs
+
+A layout spec, short for "layout specification", has no physical presence. Instead, layout specs act as containers for other layout elements by understanding how these children layout elments relate to each other.
+
+AsyncDisplayKit provides several subclasses of `ASLayoutSpec`, from a simple layout specification that insets a single child, to a more complex layout specification that arranges multiple children in varying stack configurations.
+
+### Layout Elements
+
+Layout specs contain and arrange layout elements.
+
+All `ASDisplayNode`s and `ASLayoutSpec`s conform to the `` protocol. This means that you can compose layout specs from both nodes and other layout specs. Cool!
+
+The `ASLayoutElement` protocol has several properties that can be used to create very complex layouts. In addition, layout specs have their own set of properties that can be used to adjust the arrangment of the layout elements.
+
+### Combine Layout Specs & Layout Elements to Make Complex UI
+
+Here you can see how `ASTextNode`s (highlighted in yellow), an `ASVideoNode` (top image) and an `ASStackLayoutSpec` ("stack layout spec") can be combined to create a complex layout.
+
+
+
+The play button on top of the `ASVideoNode` (top image) is placed using an `ASCenterLayoutSpec` ("center layout spec") and an `ASOverlayLayoutSpec` ("overlay layout spec").
+
+
+
+### Some nodes need Sizes Set
+
+
+
+Some elements have an "intrinsic size" based on their immediately available content. For example, ASTextNode can calculate its size based on its attributed string. Other nodes that have an intrinsic size include
+
+- `ASImageNode`
+- `ASTextNode`
+- `ASButtonNode`
+
+All other nodes either do not have an intrinsic size or lack an intrinsic size until their external resource is loaded. For example, an `ASNetworkImageNode` does not know its size until the image has been downloaded from the URL. These sorts of elments inlcude
+
+- `ASVideoNode`
+- `ASVideoPlayerNode`
+- `ASNetworkImageNode`
+- `ASEditableTextNode`
+
+These nodes that lack an initial intrinsic size must have an initial size set for them using an `ASRatioLayoutSpec`, an `ASAbsoluteLayoutSpec` or the size properties on the style object.
+
+### Layout Debugging
+
+Calling `-asciiArtString` on any `ASDisplayNode` or `ASLayoutSpec` returns an ascii-art representation of the object and its children. Optionally, if you set the `.debugName` on any node or layout spec, that will also included in the ascii art. An example is seen below.
+
+
+
+
+-----------------------ASStackLayoutSpec----------------------
+| -----ASStackLayoutSpec----- -----ASStackLayoutSpec----- |
+| | ASImageNode | | ASImageNode | |
+| | ASImageNode | | ASImageNode | |
+| --------------------------- --------------------------- |
+--------------------------------------------------------------
+
+
+
+
+You can also print out the style object on any `ASLayoutElement` (node or layout spec). This is especially useful when debugging the sizing properties.
+
+
+
+
+(lldb) po _photoImageNode.style
+Layout Size = min {414pt, 414pt} <= preferred {20%, 50%} <= max {414pt, 414pt}
+
+
+
diff --git a/docs/_docs/layout2-web-flexbox-differences.md b/docs/_docs/layout2-web-flexbox-differences.md
new file mode 100755
index 00000000..641e5953
--- /dev/null
+++ b/docs/_docs/layout2-web-flexbox-differences.md
@@ -0,0 +1,21 @@
+---
+title: Web Flexbox Differences
+layout: docs
+permalink: /docs/layout2-web-flexbox-differences.html
+---
+
+The goal of AsyncDisplayKit's Layout API is *not* to re-implement all of CSS. It only targets a subset of CSS and Flexbox container, and there are no plans to implement support for tables, floats, or any other CSS concepts. The AsyncDisplayKit Layout API also does not plan to support styling properties which do not affect layout such as color or background properties.
+
+The layout system tries to stay as close as possible to CSS. There are, however, certain cases where it differs from the web, these include:
+
+### Naming properties
+
+Certain properties have a different naming as on the web. For example `min-height` equivalent is the `minHeight` property. The full list of properties that control layout is documented in the Layout Properties section.
+
+### No margin / padding properties
+
+Layoutables don't have a padding or margin property. Instead wrapping a layoutable within an `ASInsetLayoutSpec` to apply padding or margin to the layoutable is the recommended way. See `ASInsetLayout` section for more information.
+
+### Missing features
+
+Certain features like `flexWrap` on a `ASStackLayoutSpec` are not supported currently. See Layout Properties for the full list of properties that are supported.
\ No newline at end of file
diff --git a/docs/_docs/map-node.md b/docs/_docs/map-node.md
new file mode 100755
index 00000000..6ea77387
--- /dev/null
+++ b/docs/_docs/map-node.md
@@ -0,0 +1,146 @@
+---
+title: ASMapNode
+layout: docs
+permalink: /docs/map-node.html
+prevPage: video-node.html
+nextPage: control-node.html
+---
+
+`ASMapNode` allows you to easily specify a geographic region to show to your users.
+
+### Basic Usage
+
+Let's say you'd like to show a snapshot of San Francisco. All you need are the coordinates.
+
+
+
SwiftObjective-C
+
+
+
+ASMapNode *mapNode = [[ASMapNode alloc] init];
+mapNode.preferredFrameSize = CGSizeMake(300.0, 300.0);
+
+// San Francisco
+CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194);
+
+// show 20,000 square meters
+mapNode.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000);
+
+
+
+let mapNode = ASMapNode()
+mapNode.preferredFrameSize = CGSize(width: 300.0, height: 300.0)
+
+// San Francisco
+let coord = CLLocationCoordinate2DMake(37.7749, -122.4194)
+
+// show 20,000 square meters
+mapNode.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000)
+
+
+
+
+
+
+The region value is actually just one piece of a property called `options` of type `MKMapSnapshotOptions`.
+
+
+### MKMapSnapshotOptions
+
+A map node's main components can be defined directly through its `options` property. The snapshot options object contains the following:
+
+
+ - An
MKMapCamera: used to configure altitude and pitch of the camera
+ - An
MKMapRect: basically a CGRect
+ - An
MKMapRegion: Controls the coordinate of focus, and the size around that focus to show
+ - An
MKMapType: Can be set to Standard, Satellite, etc.
+
+
+To do something like changing your map to a satellite map, you just need to create an options object and set its properties accordingly.
+
+
+
SwiftObjective-C
+
+
+
+MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
+options.mapType = MKMapTypeSatellite;
+options.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000);
+
+mapNode.options = options;
+
+
+let options = MKMapSnapshotOptions()
+options.mapType = .Satellite
+options.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000)
+
+mapNode.options = options
+
+
+
+
+Results in:
+
+
+
+One thing to note is that setting the options value will overwrite a previously set region.
+
+### Annotations
+
+To set annotations, all you need to do is assign an array of annotations to your `ASMapNode`.
+
+Say you want to show a pin directly in the middle of your map of San Francisco.
+
+
+
SwiftObjective-C
+
+
+
+MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
+annotation.coordinate = CLLocationCoordinate2DMake(37.7749, -122.4194);
+
+mapNode.annotations = @[annotation];
+
+
+let annotation = MKPointAnnotation()
+annotation.coordinate = CLLocationCoordinate2DMake(37.7749, -122.4194)
+
+mapNode.annotations = [annotation]
+
+
+
+
+
+
+No problem.
+
+### Live Map Mode
+
+Chaning your map node from a static view of some region, into a fully interactable cartographic playground is as easy as:
+
+
+
SwiftObjective-C
+
+
+
+mapNode.liveMap = YES;
+
+
+mapNode.liveMap = true
+
+
+
+
+This enables "live map mode" in which the node will use an MKMapView to render an interactive version of your map.
+
+
+
+As with UIKit views, the `MKMapView` used in live map mode is not thread-safe.
+
+### MKMapView Delegate
+
+If live map mode has been enabled and you need to react to any events associated with the map node, you can set the `mapDelegate` property. This delegate should conform to the MKMapViewDelegate protocol.
+
+
+
+
diff --git a/docs/_docs/multiplex-image-node.md b/docs/_docs/multiplex-image-node.md
new file mode 100755
index 00000000..16beda7a
--- /dev/null
+++ b/docs/_docs/multiplex-image-node.md
@@ -0,0 +1,109 @@
+---
+title: ASMultiplexImageNode
+layout: docs
+permalink: /docs/multiplex-image-node.html
+prevPage: editable-text-node.html
+---
+
+Let's say your API is out of your control and the images in your app can't be progressive jpegs but you can retrieve a few different sizes of the image asset you want to display. This is where you would use an `ASMultiplexImageNode` instead of an ASNetworkImageNode.
+
+In the following example, you're using a multiplex image node in an `ASCellNode` subclass. After initialization, you typically need to do two things. First, make sure to set `downloadsIntermediateImages` to `YES` so that the lesser quality images will be downloaded.
+
+Then, assign an array of keys to the property `imageIdentifiers`. This list should be in descending order of image quality and will be used by the node to determine what URL to call for each image it will try to load.
+
+
+
SwiftObjective-C
+
+
+
+- (instancetype)initWithURLs:(NSDictionary *)urls
+{
+ ...
+ _imageURLs = urls; // something like @{@"thumb": "/smallImageUrl", @"medium": ...}
+
+ _multiplexImageNode = [[ASMultiplexImageNode alloc] initWithCache:nil
+ downloader:[ASBasicImageDownloader sharedImageDownloader]];
+ _multiplexImageNode.downloadsIntermediateImages = YES;
+ _multiplexImageNode.imageIdentifiers = @[ @"original", @"medium", @"thumb" ];
+
+ _multiplexImageNode.dataSource = self;
+ _multiplexImageNode.delegate = self;
+ ...
+}
+
+
+
+
+init(urls: [String: NSURL]) {
+ imageURLs = urls
+
+ multiplexImageNode = ASMultiplexImageNode(cache: nil, downloader: ASBasicImageDownloader.sharedImageDownloader())
+ multiplexImageNode.downloadsIntermediateImages = true
+ multiplexImageNode.imageIdentifiers = ["original", "medium", "thumb" ]
+
+ multiplexImageNode.dataSource = self
+ multiplexImageNode.delegate = self
+ ...
+}
+
+
+
+
+
+Then, if you've set up a simple dictionary that holds the keys you provided earlier pointing to URLs of the various versions of your image, you can simply return the URL for the given key in:
+
+
+
SwiftObjective-C
+
+
+
+#pragma mark Multiplex Image Node Datasource
+
+- (NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode
+ URLForImageIdentifier:(id)imageIdentifier
+{
+ return _imageURLs[imageIdentifier];
+}
+
+
+
+func multiplexImageNode(imageNode: ASMultiplexImageNode, URLForImageIdentifier imageIdentifier: ASImageIdentifier) -> NSURL? {
+ return imageURLs[imageIdentifier]
+}
+
+
+
+
+There are also delegate methods provided to update you on things such as the progress of an image's download, when it has finished displaying etc. They're all optional so feel free to use them as necessary.
+
+For example, in the case that you want to react to the fact that a new image arrived, you can use the following delegate callback.
+
+
+
SwiftObjective-C
+
+
+
+#pragma mark Multiplex Image Node Delegate
+
+- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode
+ didUpdateImage:(UIImage *)image
+ withIdentifier:(id)imageIdentifier
+ fromImage:(UIImage *)previousImage
+ withIdentifier:(id)previousImageIdentifier;
+{
+ // this is optional, in case you want to react to the fact that a new image came in
+}
+
+
+
+func multiplexImageNode(imageNode: ASMultiplexImageNode,
+ didUpdateImage image: UIImage?,
+ withIdentifier imageIdentifier: ASImageIdentifier?,
+ fromImage previousImage: UIImage?,
+ withIdentifier previousImageIdentifier: ASImageIdentifier?) {
+ // this is optional, in case you want to react to the fact that a new image came in
+}
+
+
+
+
diff --git a/docs/_docs/network-image-node.md b/docs/_docs/network-image-node.md
new file mode 100755
index 00000000..46427d64
--- /dev/null
+++ b/docs/_docs/network-image-node.md
@@ -0,0 +1,119 @@
+---
+title: ASNetworkImageNode
+layout: docs
+permalink: /docs/network-image-node.html
+prevPage: image-node.html
+nextPage: video-node.html
+---
+
+`ASNetworkImageNode` can be used any time you need to display an image that is being hosted remotely. All you have to do is set the `.URL` property with the appropriate `NSURL` instance and the image will be asynchonously loaded and concurrently rendered for you.
+
+
+
SwiftObjective-C
+
+
+
+ASNetworkImageNode *imageNode = [[ASNetworkImageNode alloc] init];
+imageNode.URL = [NSURL URLWithString:@"https://someurl.com/image_uri"];
+
+
+
+let imageNode = ASNetworkImageNode()
+imageNode.URL = NSURL(string: "https://someurl.com/image_uri")
+
+
+
+
+### Laying Out a Network Image Node
+
+Since an `ASNetworkImageNode` has no intrinsic content size when it is created, it is necessary for you to explicitly specify how they should be laid out.
+
+Option 1: .style.preferredSize
+
+If you have a standard size you want the image node's frame size to be you can use the `.style.preferredSize` property.
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
+{
+ imageNode.preferredFrameSize = CGSizeMake(100, 200);
+ ...
+ return finalLayoutSpec;
+}
+
+
+
+override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ imageNode.preferredFrameSize = CGSize(width: 100, height: 200)
+ ...
+ return finalLayoutSpec
+}
+
+
+
+
+Option 2: ASRatioLayoutSpec
+
+This is also a perfect place to use `ASRatioLayoutSpec`. Instead of assigning a static size for the image, you can assign a ratio and the image will maintain that ratio when it has finished loading and is displayed.
+
+
+
SwiftObjective-C
+
+
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
+{
+ CGFloat ratio = 3.0/1.0;
+ ASRatioLayoutSpec *imageRatioSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:self.imageNode];
+ ...
+ return finalLayoutSpec;
+}
+
+
+
+override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
+ let ratio: CGFloat = 3.0/1.0
+ let imageRatioSpec = ASRatioLayoutSpec(ratio:ratio, child:self.imageNode)
+ ...
+ return finalLayoutSpec
+}
+
+
+
+
+### Under the Hood
+
+If you choose not to include the PINRemoteImage and PINCache dependencies you will lose progressive jpeg support and be required to include your own custom cache that conforms to ASImageCacheProtocol.
+
+#### Progressive JPEG Support
+
+Thanks to the inclusion of PINRemoteImage, network image nodes now offer full support for loading progressive JPEGs. This means that if your server provides them, your images will display quickly at a lower quality that will scale up as more data is loaded.
+
+To enable progressive loading, just set `shouldRenderProgressImages` to `YES` like so:
+
+
+
SwiftObjective-C
+
+
+
+networkImageNode.shouldRenderProgressImages = YES;
+
+
+
+networkImageNode.shouldRenderProgressImages = true
+
+
+
+
+It's important to remember that this is using one image that is progressively loaded. If your server is constrained to using regular JPEGs, but provides you with multiple versions of increasing quality, you should check out ASMultiplexImageNode instead.
+
+#### Automatic Caching
+
+`ASNetworkImageNode` now uses PINCache under the hood by default to cache network images automatically.
+
+#### GIF Support
+
+`ASNetworkImageNode` provides GIF support through `PINRemoteImage`'s beta `PINAnimatedImage`. Of note! This support will not work for local files unless `shouldCacheImage` is set to `NO`.
diff --git a/docs/_docs/node-overview.md b/docs/_docs/node-overview.md
new file mode 100755
index 00000000..4b0a5182
--- /dev/null
+++ b/docs/_docs/node-overview.md
@@ -0,0 +1,81 @@
+---
+title: Node Subclasses
+layout: docs
+permalink: /docs/node-overview.html
+prevPage: containers-overview.html
+nextPage: subclassing.html
+---
+
+AsyncDisplayKit offers the following nodes.
+
+A key advantage of using nodes over UIKit components is that **all nodes preform layout and display off of the main thread**, so that the main thread is available to immediately respond to user interaction events.
+
+
+
+ | ASDK Node |
+ UIKit Equivalent |
+
+
+ ASDisplayNode |
+ in place of UIKit's UIView
+ The root AsyncDisplayKit node, from which all other nodes inherit. |
+
+
+ ASCellNode |
+ in place of UIKit's UITableViewCell & UICollectionViewCell
+ ASCellNodes are used in ASTableNode, ASCollectionNode and ASPagerNode. |
+
+
+ ASScrollNode |
+ in place of UIKit's UIScrollView
+ This node is useful for creating a customized scrollable region that contains other nodes. |
+
+
+ ASEditableTextNode
+ ASTextNode |
+ in place of UIKit's UITextView
+ in place of UIKit's UILabel |
+
+
+ ASImageNode
+ ASNetworkImageNode
+ ASMultiplexImageNode |
+ in place of UIKit's UIImage |
+
+
+ ASVideoNode
+ ASVideoPlayerNode |
+ in place of UIKit's AVPlayerLayer
+ in place of UIKit's UIMoviePlayer |
+
+
+ ASControlNode |
+ in place of UIKit's UIControl |
+
+
+ ASButtonNode |
+ in place of UIKit's UIButton |
+
+
+ ASMapNode |
+ in place of UIKit's MKMapView |
+
+
+
+
+Despite having rough equivalencies to UIKit components, in general, AsyncDisplayKit nodes offer more advanced features and conveniences. For example, an `ASNetworkImageNode` does automatic loading and cache management, and even supports progressive jpeg and animated gifs.
+
+The `AsyncDisplayKitOverview` example app gives basic implementations of each of the nodes listed above.
+
+
+# Node Inheritance Hierarchy
+
+All AsyncDisplayKit nodes inherit from `ASDisplayNode`.
+
+
+
+The nodes highlighted in blue are synchronous wrappers of UIKit elements. For example, `ASScrollNode` wraps a `UIScrollView`, and `ASCollectionNode` wraps a `UICollectionView`. An `ASMapNode` in `liveMapMode` is a synchronous wrapper of `UIMapView`.
+
+
+
+
diff --git a/docs/_docs/overlay-layout-spec.md b/docs/_docs/overlay-layout-spec.md
new file mode 100755
index 00000000..3ddbe823
--- /dev/null
+++ b/docs/_docs/overlay-layout-spec.md
@@ -0,0 +1,7 @@
+---
+title: ASOverlayLayoutSpec
+layout: docs
+permalink: /docs/overlay-layout-spec.html
+---
+
+😑 This page is coming soon...
\ No newline at end of file
diff --git a/docs/_docs/philosophy.md b/docs/_docs/philosophy.md
new file mode 100755
index 00000000..fc16f1a0
--- /dev/null
+++ b/docs/_docs/philosophy.md
@@ -0,0 +1,47 @@
+---
+title: Philosophy
+layout: docs
+permalink: /docs/philosophy.html
+prevPage: getting-started.html
+nextPage: installation.html
+---
+
+#Asynchronous Performance Gains
+
+AsyncDisplayKit is a UI framework that was originally born from Facebook’s Paper app. It came as an answer to one of the core questions the Paper team faced. **How can you keep the main thread as clear as possible?**
+
+Nowadays, many apps have a user experience that relies heavily upon continuous gestures and physics based animations. At the very least, your UI is probably dependent on some form of scroll view. These types of user interfaces depend entirely on the main thread and are extremely sensitive to main thread stalls. **A clogged main thread means dropped frames and an unpleasant user experience.**
+
+AsyncDisplayKit Nodes are a thread-safe abstraction layer over UIViews and CALayers:
+
+
+
+You can access most view and layer properties when using nodes, the difference is that nodes are rendered concurrently by default, and measured and laid out asynchronously when used correctly!
+
+Too see asynchronous performance gains in action, check out the `examples/ASDKgram` app which compares a UIKit-implemented social media feed with an ASDK-implemented social media feed!
+
+On an iPhone 6+, the performance may not be radically different, but on a 4S, the difference is dramatic! Which leads us to ASDK's next priority...
+
+#A Great App Experience for All Users
+
+ASDK's performance gains allow you to easily design a great experience for every app user - across all devices, on all network connections.
+
+##A Great Developer Experience
+
+ASDK also strives to make the developer experience great
+- platform compatability: iOS & tvOS
+- language compatability: Objective-C & Swift
+- requires fewer lines of code to build advanced apps (see `examples/ASDKgram` for a direct comparison of a UIKit implemention of an app vs. an equivalent ASDK implementation)
+- cleaner architecture patterns
+- robust code (some really brilliant minds have worked on this for 3+ years).
+
+#Advanced Developer Tools
+
+As ASDK has grown, some of the brightest iOS engineers have contributed advanced technologies that will save you, as a developer using ASDK, development time.
+
+###Advanced Technology
+- ASRunLoopQueue
+- ASRangeController with Intelligent Preloading
+
+###Network Code Savings
+- automatic batch fetching (e.g. JSON payloads)
diff --git a/docs/_docs/placeholder-fade-duration.md b/docs/_docs/placeholder-fade-duration.md
new file mode 100755
index 00000000..f236f6a7
--- /dev/null
+++ b/docs/_docs/placeholder-fade-duration.md
@@ -0,0 +1,32 @@
+---
+title: Placeholders
+layout: docs
+permalink: /docs/placeholder-fade-duration.html
+prevPage: image-modification-block.html
+nextPage: accessibility.html
+---
+
+## ASDisplayNodes may Implement Placeholders
+
+Any `ASDisplayNode` subclass may implement the `-placeholderImage` method to provide a placeholder that covers content until a node's contents are finished displaying. To use placeholders, set `.placeholderEnabled = YES` and optionally set a `.placeholderFadeDuration`;
+
+For image drawing, use the node's `.calculatedSize` property.
+
+
+The `-placeholderImage` function may be called on a background thread, so it is important that this function is thread safe. Note that `-[UIImage imageNamed:]` is not thread safe when using image assets. Instead use `-[UIImage imageWithContentsOfFile:]`.
+
+
+
+An ideal resource for creating placeholder images, including rounded rect solid colored ones or simple square corner ones is the `UIImage+ASConvenience` category methods in ASDK.
+
+See our ancient Placeholders sample app to see this concept, first invented by the Facebook Paper team, in action.
+
+## `.neverShowPlaceholders`
+
+Hear Scott Goodson explain placeholders, `.neverShowPlaceholders` and why UIKit doesn't have them.
+
+## ASNetworkImageNode also have Default Images
+
+In _addition_ to placeholders, `ASNetworkImageNode`s also have a `.defaultImage` property. While placeholders are meant to be transient, default images will persist if the image node's `.URL` property is `nil` or if the URL fails to load.
+
+We suggest using default images for avatars, while using placeholder images for photos.
diff --git a/docs/_docs/principles.md b/docs/_docs/principles.md
new file mode 100755
index 00000000..8815a402
--- /dev/null
+++ b/docs/_docs/principles.md
@@ -0,0 +1,33 @@
+---
+title: Principles
+layout: docs
+permalink: /docs/principles.html
+---
+
+The following principles guide the design and development of the AsyncDisplayKit framework.
+
+## 1. Reliable
+
+- **What:** Behavior should match the documentation. The framework shouldn't crash in production, even when used incorrectly.
+- **Why:** If the framework is not reliable, then it cannot be used in production apps. More importantly, it will drain the morale of the engineers working on it.
+- **How:** Meaningful, stable unit tests. We will devote a significant chunk of our resources to build unit tests.
+
+## 2. Familiar
+
+- **What:** Interfaces should match industry standards such as UIKit and CSS when possible. When we diverge from these standards, the interfaces should as be intuitive and direct as possible.
+- **Why:** If the framework is not familiar, then companies will be wary about adopting it. Engineers trained in UIKit, especially junior ones, will be frustrated and unproductive.
+- **How:** Compare API to other mature frameworks, reach out to users when developing new API to get feedback. Be generous with abstraction layers – as long as we don't sacrifice Reliable.
+
+## 3. Lean
+
+- **What:** Speed and memory conservation should be industry-leading, the API should be concise, and implementation code should be short and organized.
+- **Why:** Performance is at the heart of AsyncDisplayKit. It's what we do and we do it better than anyone else. In addition, a concise codebase and API are easier to maintain and learn. Plus it's just the right thing to do.
+- **How:** Look for opportunities to improve performance. Think about the performance implications of each line of code. Dedicate resources to refactoring. Build tools to gather and expose performance metrics.
+
+## 4. Bold
+
+- **What:** Ambitious features, such as animated layout transitioning or our visibility-depth system, should be added from time to time.
+- **Why:** Cutting-edge, never-before-seen tech gets people excited about the framework, and can raise the bar for the entire industry. They really move the needle on the user experience in subtle ways. Plus it's fun!
+- **How:** Propose crazy ideas. See them through – ensure they get into the workflow and get resources allocated for them.
+
+
\ No newline at end of file
diff --git a/docs/_docs/relative-layout-spec.md b/docs/_docs/relative-layout-spec.md
new file mode 100755
index 00000000..a9e6a157
--- /dev/null
+++ b/docs/_docs/relative-layout-spec.md
@@ -0,0 +1,7 @@
+---
+title: ASRelativeLayoutSpec
+layout: docs
+permalink: /docs/relative-layout-spec.html
+---
+
+😑 This page is coming soon...
\ No newline at end of file
diff --git a/docs/_docs/resources.md b/docs/_docs/resources.md
new file mode 100755
index 00000000..503e556d
--- /dev/null
+++ b/docs/_docs/resources.md
@@ -0,0 +1,49 @@
+---
+title: Resources
+layout: docs
+permalink: /docs/resources.html
+prevPage: getting-started.html
+nextPage: installation.html
+---
+
+### Slack
+
+Join 700+ AsyncDisplayKit developers and the AsyncDisplayKit core team on Slack for real-time debugging, the latest updates, and asynchronous banter. Signup here.
+
+### Examples
+Browse through our many example projects.
+
+If you are new to AsyncDisplayKit, we recommend that you start with the ASDKgram example app which compares a photo feed implemented with UIKit to an identical feed implemented with AsyncDisplayKit. The app features:
+
+ - An infinitely scrolling home feed that demonstrates ASDK's smoother scrolling performance.
+ - A significantly sized code base to demonstrate how much less code it takes to design apps using AsyncDisplayKit.
+
+
+### Videos
+
+
+### Tutorials / Articles
+
+
+
+### Layout Resources
+AsyncDisplayKit's powerful layout system is based on the CSS FlexBox model. These sites are useful for learning the basics of this system.
+
diff --git a/docs/_docs/roadmap.md b/docs/_docs/roadmap.md
new file mode 100755
index 00000000..569f2126
--- /dev/null
+++ b/docs/_docs/roadmap.md
@@ -0,0 +1,49 @@
+---
+title: Roadmap
+layout: docs
+permalink: /docs/roadmap.html
+---
+
+This document outlines some of the upcoming plans for AsyncDisplayKit. Since AsyncDisplayKit is a fast-moving project with a small core team, this roadmap will change over time.
+
+The AsyncDisplayKit roadmap is driven by the framework's four key qualities. You can read read more about the principles here.
+
+## 2.1 Release
+
+#### Familiar
+
+- Increase investment in Swift over time.
+- Adopt a more regular release cadence.
+- Reversible 0-100% transitions for our Layout Transition API.
+
+#### Bold
+
+- Declarative collection node API. [Try it out]() and give us feedback!
+
+## 2.5+ Release
+
+#### Reliable
+
+- Audit typography features.
+
+#### Familiar
+
+- Better supplementary node support.
+
+#### Lean
+
+- True asynchronous layout.
+
+#### Bold
+
+- First class transitions with the Layout Transition API.
+- Extreme debuggability.
+- AsyncKit?
+
+## Ways to Get Involved
+
+- Connect on GitHub, Slack and Twitter.
+- Vet our documentation. Submit or suggest ways to improve.
+- Share your experience using AsyncDisplayKit. Thanks Buffer!
+- Contribute layout examples.
+- Contribute code. Try to implement one of our "Needs Volunteer" issues.
diff --git a/docs/_docs/scroll-node.md b/docs/_docs/scroll-node.md
new file mode 100755
index 00000000..344faebc
--- /dev/null
+++ b/docs/_docs/scroll-node.md
@@ -0,0 +1,81 @@
+---
+title: ASScrollNode
+layout: docs
+permalink: /docs/scroll-node.html
+prevPage: control-node.html
+nextPage: editable-text-node.html
+---
+
+`ASScrollNode` is an `ASDisplayNode` whose underlying view is an `UIScrollView`. This class offers the ability to automatically adopt its `ASLayoutSpec`'s size as the scrollable `contentSize`.
+
+### automaticallyManagesContentSize
+
+When enabled, the size calculated by the `ASScrolNode`'s layout spec defines the `.contentSize` of the scroll view. This is in contrast to most nodes, where the `layoutSpec` size is applied to the bounds (and in turn, frame). In this mode, the bounds of the scroll view always fills the parent's size.
+
+`automaticallyManagesContentSize` is useful both for subclasses of `ASScrollNode` implementing `layoutSpecThatFits:` or may also be used as the base class with `.layoutSpecBlock` set. In both cases, it is common use `.automaticallyManagesSubnodes` so that the nodes in the layout spec are added to the scrollable area automatically.
+
+With this approach there is no need to capture the layout size, use an absolute layout spec as a wrapper, or set `contentSize` anywhere in the code and it will update as the layout changes! Instead, it is very common and useful to simply return an `ASStackLayoutSpec` and the scrollable area will allow you to see all of it.
+
+### scrollableDirections
+
+This option is useful when using `automaticallyManagesContentSize`, especially if you want horizontal content (because the default is vertical).
+
+This property controls how the `constrainedSize` is interpreted when sizing the content. Options include:
+
+
+
+ | Vertical |
+ The `constrainedSize` is interpreted as having unbounded `.height` (`CGFLOAT_MAX`), allowing stacks and other content in the layout spec to expand and result in scrollable content. |
+
+
+ | Horizontal |
+ The `constrainedSize` is interpreted as having unbounded `.width` (`CGFLOAT_MAX`). |
+
+
+ | Vertical & Horizontal |
+ The `constrainedSize` is interpreted as unbounded in both directions. |
+
+
+
+### Example
+
+In case you're not familiar with scroll views, they are basically windows into content that would take up more space than can fit in that area.
+
+Say you have a giant image, but you only want to take up 200x200 pts on the screen.
+
+
+
SwiftObjective-C
+
+
+
+// NOTE: If you are using a horizontal stack, set scrollNode.scrollableDirections.
+ASScrollNode *scrollNode = [[ASScrollNode alloc] init];
+scrollNode.automaticallyManagesSubnodes = YES;
+scrollNode.automaticallyManagesContentSize = YES;
+
+scrollNode.layoutSpecBlock = ^(ASDisplayNode *node, ASSizeRange constrainedSize){
+ ASStackLayoutSpec *stack = [ASStackLayoutSpec verticalStackLayoutSpec];
+ // Add children to the stack.
+ return stack;
+};
+
+
+
+// NOTE: If you are using a horizontal stack, set scrollNode.scrollableDirections.
+
+let scrollNode = ASScrollNode()
+scrollNode.automaticallyManagesSubnodes = true
+scrollNode.automaticallyManagesContentSize = true
+
+scrollNode.layoutSpecBlock = { node, constrainedSize in
+ let stack = ASStackLayoutSpec.vertical()
+ // Add children to the stack.
+ return stack
+}
+
+
+
+
+
+As you can see, the `scrollNode`'s underlying view is a `ASScrollNode`.
+
diff --git a/docs/_docs/static-layout-spec.md b/docs/_docs/static-layout-spec.md
new file mode 100755
index 00000000..01318bf4
--- /dev/null
+++ b/docs/_docs/static-layout-spec.md
@@ -0,0 +1,7 @@
+---
+title: ASStaticLayoutSpec
+layout: docs
+permalink: /docs/static-layout-spec.html
+---
+
+😑 This page is coming soon...
\ No newline at end of file
diff --git a/docs/_docs/subclassing.md b/docs/_docs/subclassing.md
new file mode 100755
index 00000000..e284f252
--- /dev/null
+++ b/docs/_docs/subclassing.md
@@ -0,0 +1,110 @@
+---
+title: Subclassing
+layout: docs
+permalink: /docs/subclassing.html
+prevPage: containers-overview.html
+nextPage: faq.html
+---
+The most important distinction when creating a subclass is whether you writing an ASViewController or an ASDisplayNode. This sounds obvious, but because some of these differences are subtle, it is important to keep this top of mind.
+
+## ASDisplayNode
+
+While subclassing nodes is similar to writing a UIView subclass, there are a few guidelines to follow to ensure that both that you're utilizing the framework to its full potential and that your nodes behave as expected.
+
+### `-init`
+
+This method is called on a **background thread** when using nodeBlocks. However, because no other method can run until -init is finished, it should never be necessary to have a lock in this method.
+
+The most important thing to remember is that your init method must be capable of being called on any queue. Most notably, this means you should never initialize any UIKit objects, touch the view or layer of a node (e.g. `node.layer.X` or `node.view.X`) or add any gesture recognizers in your initializer. Instead, do these things in `-didLoad`.
+
+### `-didLoad`
+
+This method is conceptually similar to UIViewController's `-viewDidLoad` method and is the point where the backing view has been loaded. It is guaranteed to be called on the **main thread** and is the appropriate place to do any UIKit things (such as adding gesture recognizers, touching the view / layer, initializing UIKIt objects).
+
+### `-layoutSpecThatFits:`
+
+This method defines the layout and does the heavy calculation on a **background thread**. This method is where you build out a layout spec object that will produce the size of the node, as well as the size and position of all subnodes. This is where you will put the majority of your layout code.
+
+The layout spec object that you create is malleable up until the point that it is return in this method. After this point, it will be immutable. It's important to remember not to cache layout specs for use later but instead to recreate them when necessary.
+
+Because it is run on a background thread, you should not set any `node.view` or `node.layer` properties here. Also, unless you know what you are doing, do not create any nodes in this method. Additionally, it is not neccessary to begin this method with a call to super, unlike other method overrides.
+
+### `-layout`
+
+The call to super in this method is where the results of the layoutSpec are applied; Right after the call to super in this method, the layout spec will have been calculated and all subnodes will have been measured and positioned.
+
+`-layout` is conceptually similar to UIViewController's `-viewWillLayoutSubviews`. This is a good spot to change the hidden property, set view based properties if needed (not layoutable properties) or set background colors. You could put background color setting in -layoutSpecThatFits:, but there may be timing problems. If you happen to be using any UIViews, you can set their frames here. However, you can always create a node wrapper with `-initWithViewBlock:` and then size this on the background thread elsewhere.
+
+This method is called on the **main thread**. However, if you are using layout Specs, you shouldn't rely on this method too much, as it is much preferable to do layout off the main thread. Less than 1 in 10 subclasses will need this.
+
+One great use of `-layout` is for the specific case in which you want a subnode to be your exact size. E.g. when you want a collectionNode to take up the full screen. This case is not supported well by layout specs and it is often easiest to set the frame manually with a single line in this method:
+
+```
+subnode.frame = self.bounds;
+```
+
+If you desire the same effect in a ASViewController, you can do the same thing in -viewWillLayoutSubviews, unless your node is the node in initWithNode: and in that case it will do this automatically.
+
+## ASViewController
+
+An `ASViewController` is a regular `UIViewController` subclass that has special features to manage nodes. Since it is a UIViewController subclass, all methods are called on the **main thread** (and you should always create an ASViewController on the main thread).
+
+### `-init`
+
+This method is called once, at the very begining of an ASViewController's lifecycle. As with UIViewController initialization, it is best practice to **never access** `self.view` or `self.node.view` in this method as it will force the view to be created early. Instead, do any view access in -viewDidLoad.
+
+ASViewController's designated initializer is `initWithNode:`. A typical initializer will look something like the code below. Note how the ASViewController's node is created _before_ calling super. An ASViewController manages a node similarly to how a UIViewController manages a view, but the initialization is slightly different.
+
+
+
+
SwiftObjective-C
+
+
+
+- (instancetype)init
+{
+ _pagerNode = [[ASPagerNode alloc] init];
+ self = [super initWithNode:_pagerNode];
+
+ // setup any instance variables or properties here
+ if (self) {
+ _pagerNode.dataSource = self;
+ _pagerNode.delegate = self;
+ }
+
+ return self;
+}
+
+
+init() {
+ let pagerNode = ASPagerNode()
+ super.init(node: pagerNode)
+
+ pagerNode.setDataSource(self)
+ pagerNode.setDelegate(self)
+}
+
+
+
+
+### `-loadView`
+
+We recommend that you do not use this method because it is has no particular advantages over `-viewDidLoad` and has some disadvantages. However, it is safe to use as long as you do not set the `self.view` property to a different value. The call to [super loadView] will set it to the `node.view` for you.
+
+### `-viewDidLoad`
+
+This method is called once in a ASViewController's lifecycle, immediately after `-loadView`. This is the earliest time at which you should access the node's view. It is a great spot to put any **setup code that should only be run once and requires access to the view/layer**, such as adding a gesture recognizer.
+
+Layout code should never be put in this method, because it will not be called again when geometry changes. Note this is equally true for UIViewController; it is bad practice to put layout code in this method even if you don't currently expect geometry changes.
+
+### `-viewWillLayoutSubviews`
+
+This method is called at the exact same time as a node's `-layout` method and it may be called multiple times in a ASViewController's lifecycle; it will be called whenever the bounds of the ASViewController's node are changed (including rotation, split screen, keyboard presentation) as well as when there are changes to the hierarchy (children being added, removed, or changed in size).
+
+For consistency, it is best practice to put all layout code in this method. Because it is not called very frequently, even code that does not directly depend on the size belongs here.
+
+### `-viewWillAppear:` / `-viewDidDisappear:`
+
+These methods are called just before the ASViewController's node appears on screen (the earliest time that it is visible) and just after it is removed from the view hierarchy (the earliest time that it is no longer visible). These methods provide a good opportunity to start or stop animations related to the presentation or dismissal of your controller. This is also a good place to make a log of a user action.
+
+Although these methods may be called multiple times and geometry information is available, they are not called for all geometry changes and so should not be used for core layout code (beyond setup required for specific animations).
diff --git a/docs/_docs/subtree-rasterization.md b/docs/_docs/subtree-rasterization.md
new file mode 100755
index 00000000..a086cea3
--- /dev/null
+++ b/docs/_docs/subtree-rasterization.md
@@ -0,0 +1,26 @@
+---
+title: Subtree Rasterization
+layout: docs
+permalink: /docs/subtree-rasterization.html
+prevPage: layer-backing.html
+nextPage: synchronous-concurrency.html
+---
+
+Flattening an entire view hierarchy into a single layer improves performance, but with UIKit, comes with a hit to maintainability and hierarchy-based reasoning.
+
+With all AsyncDisplayKit nodes, enabling precompositing is as simple as:
+
+
+
SwiftObjective-C
+
+
+rootNode.shouldRasterizeDescendants = YES;
+
+
+rootNode.shouldRasterizeDescendants = true
+
+
+
+
+
+This line will cause the entire node hierarchy from that point on to be rendered into one layer.
diff --git a/docs/_docs/synchronous-concurrency.md b/docs/_docs/synchronous-concurrency.md
new file mode 100755
index 00000000..e0d9053d
--- /dev/null
+++ b/docs/_docs/synchronous-concurrency.md
@@ -0,0 +1,28 @@
+---
+title: Synchronous Concurrency
+layout: docs
+permalink: /docs/synchronous-concurrency.html
+prevPage: subtree-rasterization.html
+nextPage: corner-rounding.html
+---
+
+Both `ASViewController` and `ASCellNode` have a property called `neverShowPlaceholders`.
+
+By setting this property to YES, the main thread will be blocked until display has completed for the cell or view controller's view.
+
+Using this option does not eliminate all of the performance advantages of AsyncDisplayKit. Normally, a given node has been preloading and is almost done when it reaches the screen, so the blocking time is very short. Even if the rangeTuningParameters are set to 0 this option outperforms UIKit. While the main thread is waiting, all subnode display executes concurrently, thus synchronous concurrency.
+
+
+
SwiftObjective-C
+
+
+node.neverShowPlaceholders = YES;
+
+
+node.neverShowPlaceholders = true
+
+
+
+
+
+Usually, if a cell hasn't finished its display pass before it has reached the screen it will show placeholders until it has drawing its content. Setting this option to YES makes your scrolling node or ASViewController act more like UIKit, and in fact makes AsyncDisplayKit scrolling visually indistinguishable from UIKit's, except that it's faster.
diff --git a/docs/_docs/team.md b/docs/_docs/team.md
new file mode 100755
index 00000000..b4351ed8
--- /dev/null
+++ b/docs/_docs/team.md
@@ -0,0 +1,44 @@
+---
+title: Pinterest Team
+layout: docs
+permalink: /docs/team.html
+---
+
+
+
+  |
+ Scott Goodson (@appleguy) is an original author of AsyncDisplayKit and, most recently, a driving force behind making Pinterest's design vision a reality with the recent rewrite of the iOS app.
+ Previously, Scott managed the Facebook Paper and Instagram iOS engineering teams, and helped lead the native code rewrite of the core Facebook iOS app. He also spent four years at Apple where he was one of the first ten engineers to work on iPhone OS 1.0, and developed apps like Stocks and Calculator.
+ Scott is deeply passionate about building AsyncDisplayKit into a framework that allows effortless development of polished and performant apps that serve all users, regardless of device age, internet connection, or language. |
+
+
+  |
+ Michael Schneider (@maicki) is especially passionate about API design and recently led the re-architecture of the layout API for the 2.0 release. As our resident layout expert, Michael volunteers much of his own time to help developers on ASDK's public slack channel. Previous, Michael worked on Pocket for iOS, Mac and Chrome and the Instapaper Mac app. |
+
+
+  |
+ Huy Nguyen (@nguyenhuy ) joined the Pinterest team after authoring AsyncDisplayKit's automatic layout feature, which has become the foundation for the AsyncDisplayKit's 2.0 release. To date, the Layout API has been the largest contribution to the framework by a community member! |
+
+
+  |
+ Garrett Moon (@garrettmoon ) is the fearless leader of Pinterest's framework team. He also authored PINRemoteImage - a threadsafe, performant, feature rich image fetcher, and PINCache, a non-deadlocking fork of TMCache. Both are used as the backing store for ASNetworkImageNode. |
+
+
+  |
+ Adlai ("Ad-lee") Holler (@adlai-holler) joined the Pinterest team after making major contributions to the framework while writing Tripstr in Swift with AsyncDisplayKit.
+ |
+
+
+
+# Join us!
+
+We are looking for senior developers familiar with AsyncDisplayKit to join our team!
+
+We have an exciting roadmap that we believe will continue to push the boundaries of what is possible on the iOS platform, while making the framework easier to use than ever before.
+
+As part of the team, you would work on AsyncDisplayKit, [PINRemoteImage](https://github.com/pinterest/PINRemoteImage), and [PINCache](https://github.com/pinterest/PINCache) (the backing store for ASNetworkImageNode), while using all three in Pinterest's [app](https://itunes.apple.com/us/app/pinterest/id429047995).
+
+One interesting thing to note is that Pinterest does not have an internal fork of AsyncDisplayKit. Everything is developed on master, with release branches cut from master only a few weeks before our public application launches. This allows us to move exceptionally quickly in developing and launching improvements to millions of users.
+
+Sound interesting?
+Send us an email at AsyncDisplayKit(at)gmail.com.
diff --git a/docs/_docs/text-cell-node.md b/docs/_docs/text-cell-node.md
new file mode 100755
index 00000000..e9d0438b
--- /dev/null
+++ b/docs/_docs/text-cell-node.md
@@ -0,0 +1,45 @@
+---
+title: ASTextCellNode
+layout: docs
+permalink: /docs/text-cell-node.html
+prevPage: cell-node.html
+nextPage: control-node.html
+---
+
+ASTextCellNode is a simple ASCellNode subclass you can use when all you need is a cell with styled text.
+
+
+
SwiftObjective-C
+
+
+ASTextCellNode *textCell = [[ASTextCellNode alloc]
+ initWithAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"SomeFont" size:16.0]} insets:UIEdgeInsetsMake(8, 16, 8, 16)];
+
+
+let textCellNode = ASTextCellNode(attributes: [NSFontAttributeName: UIFont(name: "SomeFont", size: 16.0)],
+ insets: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16))
+
+
+
+
+The text can be configured on initialization or after the fact.
+
+
+
SwiftObjective-C
+
+
+ASTextCellNode *textCell = [[ASTextCellNode alloc] init];
+
+textCellNode.text = @"Some dang ol' text";
+textCellNode.attributes = @{NSFontAttributeName: [UIFont fontWithName:@"SomeFont" size:16.0]};
+textCellNode.insets = UIEdgeInsetsMake(8, 16, 8, 16);
+
+
+let textCellNode = ASTextCellNode()
+
+textCellNode.text = "Some dang ol' text"
+textCellNode.attributes = [NSFontAttributeName: UIFont(name: "SomeFont", size: 16.0)]
+textCellNode.insets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
+
+
+
\ No newline at end of file
diff --git a/docs/_docs/text-node.md b/docs/_docs/text-node.md
new file mode 100755
index 00000000..1210a512
--- /dev/null
+++ b/docs/_docs/text-node.md
@@ -0,0 +1,147 @@
+---
+title: ASTextNode
+layout: docs
+permalink: /docs/text-node.html
+prevPage: button-node.html
+nextPage: image-node.html
+---
+
+`ASTextNode` is AsyncDisplayKit's main text node and can be used any time you would normally use a `UILabel`. It includes full rich text support and is a subclass of `ASControlNode` meaning it can be used any time you would normally create a UIButton with just its titleLabel set.
+
+### Basic Usage
+`ASTextNode`'s interface should be familiar to anyone who's used a `UILabel`. The first difference you may notice, is that text node's only use attributed strings instead of having the option of using a plain string.
+
+
+
SwiftObjective-C
+
+
+
+NSDictionary *attrs = @{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:12.0f] };
+NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Hey, here's some text." attributes:attrs];
+
+_node = [[ASTextNode alloc] init];
+_node.attributedString = string;
+
+
+
+let attrs = [NSFontAttributeName: UIFont(name: "HelveticaNeue", size: 12.0)]
+let string = NSAttributedString(string: "Hey, here's some text.", attributes: attrs)
+
+node = ASTextNode()
+node.attributedString = string
+
+
+
+
+As you can see, to create a basic text node, all you need to do is use a standard alloc-init and then set up the attributed string for the text you wish to display.
+
+### Truncation
+
+In any case where you need your text node to fit into a space that is smaller than what would be necessary to display all the text it contains, as much as possible will be shown, and whatever is cut off will be replaced with a truncation string.
+
+
+
+
SwiftObjective-C
+
+
+
+_textNode = [[ASTextNode alloc] init];
+_textNode.attributedString = string;
+_textNode.truncationAttributedString = [[NSAttributedString alloc]
+ initWithString:@"¶¶¶"];
+
+
+
+textNode = ASTextNode()
+textNode.attributedString = string
+textNode.truncationAttributedString = NSAttributedString(string: "¶¶¶")
+
+
+
+
+This results in something like:
+
+
+
+By default, the truncation string will be "…" so you don't need to set it if that's all you need.
+
+
+### Link Attributes
+
+In order to designate chunks of your text as a link, you first need to set the `linkAttributes` array to an array of strings which will be used as keys of links in your attributed string. Then, when setting up the attributes of your string, you can use these keys to point to appropriate `NSURL`s.
+
+
+
SwiftObjective-C
+
+
+
+_textNode.linkAttributeNames = @[ kLinkAttributeName ];
+
+NSString *blurb = @"kittens courtesy placekitten.com \U0001F638";
+NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb];
+[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Light" size:16.0f] range:NSMakeRange(0, blurb.length)];
+[string addAttributes:@{
+ kLinkAttributeName: [NSURL URLWithString:@"http://placekitten.com/"],
+ NSForegroundColorAttributeName: [UIColor grayColor],
+ NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
+ }
+ range:[blurb rangeOfString:@"placekitten.com"]];
+_textNode.attributedString = string;
+
+
+
+let blurb: NSString = "kittens courtesy placekitten.com 😸"
+let attributedString = NSMutableAttributedString(string: blurb as String)
+
+attributedString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue-Light", size: 16.0)!, range: NSRange(location: 0, length: blurb.length))
+
+attributedString.addAttributes([kLinkAttributeName: NSURL(string: "http://placekitten.com/")!,
+ NSForegroundColorAttributeName: UIColor.grayColor(),
+ NSUnderlineStyleAttributeName: (NSUnderlineStyle.StyleSingle.rawValue | NSUnderlineStyle.PatternDashDot.rawValue)],
+ range: blurb.rangeOfString("placekitten.com"))
+textNode.attributedString = attributedString
+
+
+
+
+Which results in a light gray link with a dash-dot style underline!
+
+
+
+As you can see, it's relatively convenient to apply various styles to each link given its range in the attributed string.
+
+### ASTextNodeDelegate
+
+Conforming to `ASTextNodeDelegate` allows your class to react to various events associated with a text node. For example, if you want to react to one of your links being tapped:
+
+
+
SwiftObjective-C
+
+
+
+- (void)textNode:(ASTextNode *)richTextNode tappedLinkAttribute:(NSString *)attribute value:(NSURL *)URL atPoint:(CGPoint)point textRange:(NSRange)textRange
+{
+ // the link was tapped, open it
+ [[UIApplication sharedApplication] openURL:URL];
+}
+
+
+
+func textNode(textNode: ASTextNode, tappedLinkAttribute attribute: String, value: AnyObject, atPoint point: CGPoint, textRange: NSRange) {
+ guard let url = value as? NSURL else { return }
+
+ UIApplication.sharedApplication().openURL(url)
+}
+
+
+
+
+In a similar way, you can react to long presses and highlighting with the following methods:
+
+`– textNode:longPressedLinkAttribute:value:atPoint:textRange:`
+
+`– textNode:shouldHighlightLinkAttribute:value:atPoint:`
+
+`– textNode:shouldLongPressLinkAttribute:value:atPoint:`
+
+
diff --git a/docs/_docs/tip-1-nodeBlocks.md b/docs/_docs/tip-1-nodeBlocks.md
new file mode 100755
index 00000000..5f30316e
--- /dev/null
+++ b/docs/_docs/tip-1-nodeBlocks.md
@@ -0,0 +1,133 @@
+---
+title: Prefer `nodeBlocks` for Performance
+layout: docs
+permalink: /docs/tip-1-nodeBlocks.html
+---
+
+AsyncDisplayKit’s `ASCollectionNode` replaces `UICollectionView`’s required method
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+collectionNode:cellForItemAtIndexPath:
+
+
+
+
+
+
+
+
+with your choice of **one** of the two following methods
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+// called on main thread, ASCellNode initialized on main and then returned
+collectionNode:nodeForItemAtIndexPath:
+
+OR
+
+// called on main thread, ASCellNodeBlock returned, then
+// ASCellNode initialized in background when block is called by system
+collectionNode:nodeBlockForItemAtIndexPath:
+
+
+
+
+
+
+
+
+`ASTableNode` has the same options:
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+`tableNode:nodeForRow:`
+`tableNode:nodeBlockforRow:` // preferred
+
+
+
+
+
+
+
+`ASPagerNode` does as well:
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+`pagerNode:nodeAtIndex:`
+`pagerNode:nodeBlockAtIndex:` // preferred
+
+
+
+
+
+
+
+
+We reccommend that you use nodeBlocks. Using the nodeBlock method allows table and collections to request blocks for each cell node, and execute them **concurrently** across multiple threads, which allows us to **parallelize the allocation costs** (in addition to layout measurement).
+
+This leaves our main thread more free to handle touch events and other time sensitive work, keeping our user's taps happy and responsive.
+
+### Access your data source outside of the nodeBlock
+
+Because nodeBlocks are executed on a background thread, it is very important they be thread-safe.
+
+The most important aspect to consider is accessing properties on self that may change, such as an array of data models. This can be handled safely by ensuring that any immutable state is collected above the node block.
+
+**Using the indexPath parameter to access a mutable collection inside the node block is not safe.** This is because by the time the block runs, the dataSource may have changed.
+
+Here's an example of a simple nodeBlock:
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ // data model is accessed outside of the node block
+ Board *board = [self.boards objectAtIndex:indexPath.item];
+ return ^{
+ BoardScrubberCellNode *node = [[BoardScrubberCellNode alloc] initWithBoard:board];
+ return node;
+ };
+}
+
+
+
+
+
+
+
+Note that it is okay to use the indexPath if it is used strictly for its integer values and not to index a value from a mutable data source.
+
+## Do not return nil from a nodeBlock
+
+Just as when UIKit requests a cell, returning `nil` will crash the app, so it is important to ensure a valid ASCellNode is returned for either the node or nodeBlock method. Your code should ensure that at least a blank ASCellNode is returned, but ideally the number of items reported to the collection would prevent the method from being called when there is no data to display.
\ No newline at end of file
diff --git a/docs/_docs/uicollectionview-challenges.md b/docs/_docs/uicollectionview-challenges.md
new file mode 100755
index 00000000..9f554df2
--- /dev/null
+++ b/docs/_docs/uicollectionview-challenges.md
@@ -0,0 +1,160 @@
+---
+title: UICollectionView Challenges
+layout: docs
+permalink: /docs/uicollectionview-challenges.html
+---
+
+`UICollectionView` is one of the most commonly used classes and many challenges with iOS development are related to its architecture.
+
+## How `UICollectionView` Works
+
+There are two important methods that `UICollectionView` requires.
+
+Cell Measurement
+
+For each item in the data source, the collection must know its size to understand which items should be visible at a given momement. This is provided by:
+
+
+
SwiftObjective-C
+
+
+- (CGSize)collectionView:(UICollectionView *)collectionView
+ layout:(UICollectionViewLayout *)collectionViewLayout
+ sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+
+optional func collectionView(_ collectionView: UICollectionView,
+ layout collectionViewLayout: UICollectionViewLayout,
+ sizeForItemAt indexPath: IndexPath) -> CGSize
+
+
+
+
+Although not formally named by Apple, we refer to this process as "measuring". Implementing this method is always difficult, because the view that implements the cell layout is never available at the time of this method call.
+
+This means that logic must be duplicated between the implementation of this method and the `-layoutSubviews` implementation of the cell subclass. This presents a tremendous maintainence burden, as the implementations must always match their behavior for any combination of content displayed.
+
+Additionally, once measurement is complete, there's no easy way to cache that information to use it during the layout process. As a result, expensive text measurements must be repeated.
+
+Cell Allocation
+
+Once an item reaches the screen, a view representing it is requested:
+
+
+
SwiftObjective-C
+
+
+- (UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+
+func cellForItem(at indexPath: IndexPath) -> UICollectionViewCell?
+
+
+
+
+In order to provide a cell, all subviews must be configured with the data that they are intended to display. Immediately afterwards, the layout of the cell is calculated, and finally the display (rendering) of the individual elements (text, images) contained within.
+
+
+For those who are curious, this extremely detailed
diagram shows the full process of
UICollectionView communicating with its data source and delegate to display itself.
+
+
+Limitations in `UICollectionView`'s Architecture
+
+There are several issues with the architecture outlined above:
+
+Lots of main thread work, which may degrade the user's experience, including
+
+
+- cell measurement
+- cell creation + setup / reuse
+- layout
+- display (rendering)
+
+Duplicated layout logic
+
+You must have duplicate copies of your cell sizing logic for the cell measurement and cell layout stages. For example, if you want to add a price tag to your cell, both -sizeForItemAtIndexPath and the cell's own -layoutSubviews must be aware of how to size the tag.
+
+No automatic content loading
+
+There is no easy, universal way to handle loading content such as:
+
+- data pages - such as JSON fetching
+- other info - such as images or secondary JSON requests
+
+
+## How `ASCollectionNode` works
+
+Unified Cell Measurement & Allocation
+
+AsyncDisplayKit takes both of the important collection methods explained above:
+
+
+
SwiftObjective-C
+
+
+- (UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+- (CGSize)collectionView:(UICollectionView *)collectionView
+ layout:(UICollectionViewLayout *)collectionViewLayout
+ sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+
+optional func collectionView(_ collectionView: UICollectionView,
+ layout collectionViewLayout: UICollectionViewLayout,
+ sizeForItemAt indexPath: IndexPath) -> CGSize
+
+func cellForItem(at indexPath: IndexPath) -> UICollectionViewCell?
+
+
+
+
+and replaces them with a single method*:
+
+
+
SwiftObjective-C
+
+
+- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+
+func collectionNode(collectionNode: ASCollectionNode, nodeForItemAtIndexPath indexPath: NSIndexPath) -> ASCellNode
+
+
+
+
+or with the asynchronous versions
+
+
+
SwiftObjective-C
+
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+
+func collectionNode(collectionNode: ASCollectionNode, nodeBlockForItemAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock
+
+
+
+
+*Note that there is an optional method to provide a constrained size for the cell, but it is not needed by most apps.
+
+ASCellNode, is AsyncDisplayKit's universal cell class. They are light-weight enough to be created an an earlier time in the program (concurrently in the background) and they understand how to calculate their own size. `ASCellNode` automatically caches its measurement so that it can be quickly applied during the layout pass.
+
+
+As a comparison to the diagram above, this detailed
diagram shows the full process of an
ASCollectionView communicating with its data source and delegate to display itself.. Note that
ASCollectionView is
ASCollectionNode's underlying
UICollectionView subclass.
+
+
+Benefits of AsyncDisplayKit's Architecture
+
+Elimination of all of the types of main thread work described above (cell allocation, measurement, layout, display)! In addition, all of this work is preformed concurrently on multiple threads.
+
+Because `ASCollectionNode` is aware of the position of all of its nodes, it can automatically determine when content loading is needed. The Batch Fetching API handles loading of data pages (like JSON) and Intelligent Preloading automatically manages the loading of images and text. Additionally, convenient callbacks allow implementing accurate visibility logging and secondary data model requests.
+
+Lastly, almost all of the concepts we've discussed here apply to `UITableView` / `ASTableNode` and `UIPageViewController` / `ASPagerNode`.
+
+## iOS 10 Cell Pre-fetching
+Inspired by ASDK, iOS 10 introduced a cell pre-fetching. This API increases the number of cells that the collection tracks at any given time, which helps, but isn't anywhere as performance centric as being aware of all cells in the data source.
+
+Additionally, iOS9 still constitutes a substantial precentage of most app's userbase and will not reduce in number anywhere close to as quickly as the sunset trajectory of iOS 7 and iOS 8 devices. Whereas iOS 9 is the last supported version for about a half-dozen devices, there were zero devices that were deprecated on iOS 8 and only one deivce deprecated on iOS 7.
+
+Unfortunately, these iOS 9 devices are the ones in which performance is most key!
diff --git a/docs/_docs/uicollectionviewinterop.md b/docs/_docs/uicollectionviewinterop.md
new file mode 100755
index 00000000..6f39fa15
--- /dev/null
+++ b/docs/_docs/uicollectionviewinterop.md
@@ -0,0 +1,81 @@
+---
+title: UICollectionViewCell Interoperability
+layout: docs
+permalink: /docs/uicollectionviewinterop.html
+prevPage: placeholder-fade-duration.html
+nextPage: accessibility.html
+---
+
+AsyncDisplayKit's `ASCollectionNode` offers compatibility with synchronous, standard `UICollectionViewCell` objects alongside native `ASCellNodes`.
+
+Note that these UIKit cells will **not** have the performance benefits of `ASCellNodes` (like preloading, async layout, and async drawing), even when mixed within the same `ASCollectionNode`.
+
+However, this interoperability allows developers the flexibility to test out the framework without needing to convert all of their cells at once.
+
+## Implementing Interoperability
+
+In order to use this feature, you must:
+
+
+- Conform to
ASCollectionDataSourceInterop and, optionally, ASCollectionDelegateInterop.
+- Call
registerCellClass: on the collectionNode.view (in viewDidLoad, or register an onDidLoad: block).
+- Return nil from the
nodeBlockForItem...: or nodeForItem...: method. Note: it is an error to return nil from within a nodeBlock, if you have returned a nodeBlock object.
+- Lastly, you must implement a method to provide the size for the cell. There are two ways this is done:
+
+UICollectionViewFlowLayout (incl. ASPagerNode). Implement
+ collectionNode:constrainedSizeForItemAtIndexPath:.
+- Custom collection layouts. Set
.view.layoutInspector and have it implement
+ collectionView:constrainedSizeForNodeAtIndexPath:.
+
+
+
+By default, the interop data source will only be consulted in cases where no `ASCellNode` is provided to AsyncDisplayKit. However, if .dequeuesCellsForNodeBackedItems is enabled, then the interop data source will always be consulted to dequeue cells, and will be expected to return _ASCollectionViewCells in cases where a node was provided.
+
+## CustomCollectionView Example App
+
+The [CustomCollectionView](https://github.com/facebook/AsyncDisplayKit/tree/master/examples/CustomCollectionView) example project demonstrates how to use raw `UIKit` cells alongside native `ASCellNodes`.
+
+Open the app and verify that `kShowUICollectionViewCells` is enabled in `Sample/ViewController.m`.
+
+For this example, the data source method `collectionNode:nodeBlockForItemAtIndexPath:` is setup to return nil for every third cell. When nil is returned, `ASCollectionNode` will automatically query the `cellForItemAtIndexPath:` data source method.
+
+
+
+ Swift
+ Objective-C
+
+
+
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode
+ nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ if (kShowUICollectionViewCells && indexPath.item % 3 == 1) {
+ // When enabled, return nil for every third cell and then
+ // cellForItemAtIndexPath: will be called.
+ return nil;
+ }
+
+ UIImage *image = _sections[indexPath.section][indexPath.item];
+ return ^{
+ return [[ImageCellNode alloc] initWithImage:image];
+ };
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
+ cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ return [_collectionNode.view dequeueReusableCellWithReuseIdentifier:kReuseIdentifier
+ forIndexPath:indexPath];
+}
+
+
+
+ // Click the "Edit on GitHub" button at the bottom of this
+ // page to contribute the swift code for this section. Thanks!
+
+
+
+
+Run the app to see the orange `UICollectionViewCells` interspersed every 3rd cell among the `ASCellNodes` containing images.
+
diff --git a/docs/_docs/video-node.md b/docs/_docs/video-node.md
new file mode 100755
index 00000000..699952d1
--- /dev/null
+++ b/docs/_docs/video-node.md
@@ -0,0 +1,87 @@
+---
+title: ASVideoNode
+layout: docs
+permalink: /docs/video-node.html
+prevPage: network-image-node.html
+nextPage: map-node.html
+---
+
+`ASVideoNode` provides a convenient and performant way to display videos in your app.
+
+Note: If you use `ASVideoNode` in your application, you must link `AVFoundation` since it uses `AVPlayerLayer` and other `AVFoundation` classes under the hood.
+
+### Basic Usage
+
+The easiest way to use `ASVideoNode` is to assign it an `AVAsset`.
+
+
+
SwiftObjective-C
+
+
+
+ASVideoNode *videoNode = [[ASVideoNode alloc] init];
+
+AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://www.w3schools.com/html/mov_bbb.mp4"]];
+videoNode.asset = asset;
+
+
+
+let videoNode = ASVideoNode()
+
+let asset = AVAsset(URL: NSURL(string: "http://www.w3schools.com/html/mov_bbb.mp4"))
+videoNode.asset = asset
+
+
+
+
+### Autoplay, Autorepeat, and Muting
+
+You can configure the way your video node reacts to various events with a few simple `BOOL`s.
+
+If you'd like your video to automaticaly play when it enters the visible range, set the `shouldAutoplay` property to `YES`. Setting `shouldAutoRepeat` to `YES` will cause the video to loop indefinitely, and, of course, setting `muted` to `YES` will turn the video's sound off.
+
+To set up a node that automatically plays once silently, you would just do the following.
+
+
+
SwiftObjective-C
+
+
+
+videoNode.shouldAutoplay = YES;
+videoNode.shouldAutorepeat = NO;
+videoNode.muted = YES;
+
+
+videoNode.shouldAutoplay = true
+videoNode.shouldAutorepeat = false
+videoNode.muted = true
+
+
+
+
+### Placeholder Image
+
+Since video nodes inherit from `ASNetworkImageNode`, you can use the `URL` property to assign a placeholder image. If you decide not to, the first frame of your video will automatically decoded and used as the placeholder instead.
+
+
+
+
+### ASVideoNode Delegate
+
+There are a ton of delegate methods available to you that allow you to react to what's happening with your video. For example, if you want to react to the player's state changing, you can use:
+
+
+
SwiftObjective-C
+
+
+
+- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState;
+
+
+videoNode(videoNode:willChangePlayerState:toState:)
+
+
+
+
+The easiest way to see them all is to take a look at the `ASVideoNode` header file.
+
diff --git a/docs/_includes/analytics.html b/docs/_includes/analytics.html
new file mode 100755
index 00000000..71dbfbce
--- /dev/null
+++ b/docs/_includes/analytics.html
@@ -0,0 +1,10 @@
+
diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html
new file mode 100755
index 00000000..5a2e8997
--- /dev/null
+++ b/docs/_includes/footer.html
@@ -0,0 +1,19 @@
+
+
+
+
+