Skip to content

Commit 5a4d569

Browse files
authored
Ensure an ASM enabled node applies its pending layout when enters preload state (facebookarchive#706)
This makes sure subnodes are inserted and start preloading right away, instead of waiting until the next layout pass of the supernode. Fixes facebookarchive#693.
1 parent 008a1ce commit 5a4d569

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [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)
1717
- [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)
1818
- Add new unit testing to the layout engine. [Adlai Holler](https://github.com/Adlai-Holler) [#424](https://github.com/TextureGroup/Texture/pull/424)
19+
- [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)
1920

2021
## 2.6
2122
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)

Source/ASDisplayNode.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3079,6 +3079,20 @@ - (void)didEnterPreloadState
30793079
{
30803080
ASDisplayNodeAssertMainThread();
30813081
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
3082+
3083+
if (self.automaticallyManagesSubnodes) {
3084+
// Tell the node to apply its applicable pending layout, if any, so that its subnodes are inserted/deleted
3085+
// and start preloading right away.
3086+
//
3087+
// If this node has an up-to-date layout (and subnodes), calling layoutIfNeeded will be fast.
3088+
//
3089+
// If this node doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur
3090+
// (see __layout and _u_measureNodeWithBoundsIfNecessary:).
3091+
// This scenario should be uncommon, and running a measurement pass here is a fine trade-off because preloading
3092+
// any time after this point would be late.
3093+
[self layoutIfNeeded];
3094+
}
3095+
30823096
[_interfaceStateDelegate didEnterPreloadState];
30833097
}
30843098

Tests/ASDisplayNodeImplicitHierarchyTests.m

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// ASDisplayNodeImplicitHierarchyTests.m
33
// Texture
44
//
5-
// Created by Levi McCallum on 2/1/16.
6-
//
75
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
86
// This source code is licensed under the BSD-style license found in the
97
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
@@ -20,6 +18,7 @@
2018
#import <XCTest/XCTest.h>
2119

2220
#import <AsyncDisplayKit/AsyncDisplayKit.h>
21+
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
2322
#import "ASDisplayNodeTestsHelper.h"
2423

2524
@interface ASSpecTestDisplayNode : ASDisplayNode
@@ -101,6 +100,47 @@ - (void)testInitialNodeInsertionWithOrdering
101100
XCTAssertEqual(node.subnodes[4], node5);
102101
}
103102

103+
- (void)testInitialNodeInsertionWhenEnterPreloadState
104+
{
105+
static CGSize kSize = {100, 100};
106+
107+
static NSInteger subnodeCount = 5;
108+
NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray arrayWithCapacity:subnodeCount];
109+
for (NSInteger i = 0; i < subnodeCount; i++) {
110+
ASDisplayNode *subnode = [[ASDisplayNode alloc] init];
111+
// As we will involve a stack spec we have to give the nodes an intrinsic content size
112+
subnode.style.preferredSize = kSize;
113+
[subnodes addObject:subnode];
114+
}
115+
116+
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
117+
node.automaticallyManagesSubnodes = YES;
118+
node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) {
119+
ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[subnodes[3]]];
120+
121+
ASStackLayoutSpec *stack1 = [[ASStackLayoutSpec alloc] init];
122+
[stack1 setChildren:@[subnodes[0], subnodes[1]]];
123+
124+
ASStackLayoutSpec *stack2 = [[ASStackLayoutSpec alloc] init];
125+
[stack2 setChildren:@[subnodes[2], absoluteLayout]];
126+
127+
return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[stack1, stack2, subnodes[4]]];
128+
};
129+
130+
ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)));
131+
[node recursivelySetInterfaceState:ASInterfaceStatePreload];
132+
133+
// No premature view allocation
134+
XCTAssertFalse(node.isNodeLoaded);
135+
// Subnodes should be inserted, laid out and entered preload state
136+
XCTAssertTrue([subnodes isEqualToArray:node.subnodes]);
137+
for (NSInteger i = 0; i < subnodeCount; i++) {
138+
ASDisplayNode *subnode = subnodes[i];
139+
XCTAssertTrue(CGSizeEqualToSize(kSize, subnode.bounds.size));
140+
XCTAssertTrue(ASInterfaceStateIncludesPreload(subnode.interfaceState));
141+
}
142+
}
143+
104144
- (void)testCalculatedLayoutHierarchyTransitions
105145
{
106146
static CGSize kSize = {100, 100};

0 commit comments

Comments
 (0)