Skip to content

Commit

Permalink
Ensure an ASM enabled node applies its pending layout when enters pre…
Browse files Browse the repository at this point in the history
…load state (#706)

This makes sure subnodes are inserted and start preloading right away, instead of waiting until the next layout pass of the supernode. Fixes #693.
  • Loading branch information
nguyenhuy committed Dec 8, 2017
1 parent 008a1ce commit 5a4d569
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- [ASScrollNode] Ensure the node respects the given size range while calculating its layout. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy)
- [ASScrollNode] Invalidate the node's calculated layout if its scrollable directions changed. Also add unit tests for the class. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy)
- Add new unit testing to the layout engine. [Adlai Holler](https://github.com/Adlai-Holler) [#424](https://github.com/TextureGroup/Texture/pull/424)
- [Automatic Subnode Management] Nodes with ASM enabled now insert/delete their subnodes as soon as they enter preload state, so the subnodes can preload too. [Huy Nguyen](https://github.com/nguyenhuy) [#706](https://github.com/TextureGroup/Texture/pull/706)

## 2.6
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
Expand Down
14 changes: 14 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,20 @@ - (void)didEnterPreloadState
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);

if (self.automaticallyManagesSubnodes) {
// Tell the node to apply its applicable pending layout, if any, so that its subnodes are inserted/deleted
// and start preloading right away.
//
// If this node has an up-to-date layout (and subnodes), calling layoutIfNeeded will be fast.
//
// If this node doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur
// (see __layout and _u_measureNodeWithBoundsIfNecessary:).
// This scenario should be uncommon, and running a measurement pass here is a fine trade-off because preloading
// any time after this point would be late.
[self layoutIfNeeded];
}

[_interfaceStateDelegate didEnterPreloadState];
}

Expand Down
44 changes: 42 additions & 2 deletions Tests/ASDisplayNodeImplicitHierarchyTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// ASDisplayNodeImplicitHierarchyTests.m
// Texture
//
// Created by Levi McCallum on 2/1/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
Expand All @@ -20,6 +18,7 @@
#import <XCTest/XCTest.h>

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import "ASDisplayNodeTestsHelper.h"

@interface ASSpecTestDisplayNode : ASDisplayNode
Expand Down Expand Up @@ -101,6 +100,47 @@ - (void)testInitialNodeInsertionWithOrdering
XCTAssertEqual(node.subnodes[4], node5);
}

- (void)testInitialNodeInsertionWhenEnterPreloadState
{
static CGSize kSize = {100, 100};

static NSInteger subnodeCount = 5;
NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray arrayWithCapacity:subnodeCount];
for (NSInteger i = 0; i < subnodeCount; i++) {
ASDisplayNode *subnode = [[ASDisplayNode alloc] init];
// As we will involve a stack spec we have to give the nodes an intrinsic content size
subnode.style.preferredSize = kSize;
[subnodes addObject:subnode];
}

ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
node.automaticallyManagesSubnodes = YES;
node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) {
ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[subnodes[3]]];

ASStackLayoutSpec *stack1 = [[ASStackLayoutSpec alloc] init];
[stack1 setChildren:@[subnodes[0], subnodes[1]]];

ASStackLayoutSpec *stack2 = [[ASStackLayoutSpec alloc] init];
[stack2 setChildren:@[subnodes[2], absoluteLayout]];

return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[stack1, stack2, subnodes[4]]];
};

ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)));
[node recursivelySetInterfaceState:ASInterfaceStatePreload];

// No premature view allocation
XCTAssertFalse(node.isNodeLoaded);
// Subnodes should be inserted, laid out and entered preload state
XCTAssertTrue([subnodes isEqualToArray:node.subnodes]);
for (NSInteger i = 0; i < subnodeCount; i++) {
ASDisplayNode *subnode = subnodes[i];
XCTAssertTrue(CGSizeEqualToSize(kSize, subnode.bounds.size));
XCTAssertTrue(ASInterfaceStateIncludesPreload(subnode.interfaceState));
}
}

- (void)testCalculatedLayoutHierarchyTransitions
{
static CGSize kSize = {100, 100};
Expand Down

0 comments on commit 5a4d569

Please sign in to comment.