Implement simple, in-order add/remove subnode support when changing layout specs

This commit is contained in:
Levi McCallum
2016-02-01 17:49:48 -08:00
parent bd1de07c77
commit d168ec78ce
3 changed files with 145 additions and 18 deletions
@@ -11,19 +11,35 @@
#import "ASDisplayNode.h"
#import "ASDisplayNode+Beta.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASStaticLayoutSpec.h"
#import "ASStackLayoutSpec.h"
@interface ASSpecTestDisplayNode : ASDisplayNode
@property (copy, nonatomic) ASLayoutSpec * (^layoutSpecBlock)(ASSizeRange constrainedSize);
@property (copy, nonatomic) ASLayoutSpec * (^layoutSpecBlock)(ASSizeRange constrainedSize, NSNumber *layoutState);
/**
Simple state identifier to allow control of current spec inside of the layoutSpecBlock
*/
@property (strong, nonatomic) NSNumber *layoutState;
@end
@implementation ASSpecTestDisplayNode
- (instancetype)init
{
self = [super init];
if (self) {
_layoutState = @1;
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
return self.layoutSpecBlock(constrainedSize);
return self.layoutSpecBlock(constrainedSize, _layoutState);
}
@end
@@ -49,18 +65,65 @@
XCTAssert([ASDisplayNode usesImplicitHierarchyManagement]);
}
- (void)testInitialNodeInsertion
- (void)testInitialNodeInsertionWithOrdering
{
ASDisplayNode *node1 = [[ASDisplayNode alloc] init];
ASDisplayNode *node2 = [[ASDisplayNode alloc] init];
ASDisplayNode *node3 = [[ASDisplayNode alloc] init];
ASDisplayNode *node4 = [[ASDisplayNode alloc] init];
ASDisplayNode *node5 = [[ASDisplayNode alloc] init];
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
node.layoutSpecBlock = ^(ASSizeRange constrainedSize){
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node1, node2]];
node.layoutSpecBlock = ^(ASSizeRange constrainedSize, NSNumber *layoutState) {
ASStaticLayoutSpec *staticLayout = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node4]];
ASStackLayoutSpec *stack1 = [[ASStackLayoutSpec alloc] init];
[stack1 setChildren:@[node1, node2]];
ASStackLayoutSpec *stack2 = [[ASStackLayoutSpec alloc] init];
[stack2 setChildren:@[node3, staticLayout]];
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack1, stack2, node5]];
};
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layout]; // Layout immediately
XCTAssertEqual(node.subnodes[0], node5);
XCTAssertEqual(node.subnodes[1], node1);
XCTAssertEqual(node.subnodes[2], node2);
XCTAssertEqual(node.subnodes[3], node3);
XCTAssertEqual(node.subnodes[4], node4);
}
- (void)testCalculatedLayoutHierarchyTransitions
{
ASDisplayNode *node1 = [[ASDisplayNode alloc] init];
ASDisplayNode *node2 = [[ASDisplayNode alloc] init];
ASDisplayNode *node3 = [[ASDisplayNode alloc] init];
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
node.layoutSpecBlock = ^(ASSizeRange constrainedSize, NSNumber *layoutState){
if ([layoutState isEqualToNumber:@1]) {
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node1, node2]];
} else {
ASStackLayoutSpec *stackLayout = [[ASStackLayoutSpec alloc] init];
[stackLayout setChildren:@[node3, node2]];
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node1, stackLayout]];
}
};
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layout]; // Layout immediately
XCTAssertEqual(node.subnodes[0], node1);
XCTAssertEqual(node.subnodes[1], node2);
node.layoutState = @2;
[node invalidateCalculatedLayout]; // TODO(levi): Look into a way where measureWithSizeRange resizes when a new hierarchy is introduced but the size has not changed
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layout]; // Layout immediately
XCTAssertEqual(node.subnodes[0], node1);
XCTAssertEqual(node.subnodes[1], node3);
XCTAssertEqual(node.subnodes[2], node2);
}
@end