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

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 7, 2017
1 parent 0dc7002 commit e5ca7f6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,16 @@ - (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 enter preload state as well.
// 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 be forced to occur (see __layout and _u_measureNodeWithBoundsIfNecessary:).
// The situation should be uncommon, and forcing is necessary to kick off preloading on subnodes.
[self layoutIfNeeded];
}

[_interfaceStateDelegate didEnterPreloadState];
}

Expand Down
42 changes: 42 additions & 0 deletions Tests/ASDisplayNodeImplicitHierarchyTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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 +102,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 e5ca7f6

Please sign in to comment.