diff --git a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m index 6623c55205..9c5ee21c1d 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m @@ -12,6 +12,7 @@ #import +#import "ASDisplayNodeTestsHelper.h" #import "ASDisplayNode.h" #import "ASDisplayNode+Beta.h" #import "ASDisplayNode+Subclasses.h" @@ -88,7 +89,10 @@ return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[stack1, stack2, node5]]; }; - [node layoutThatFits:ASSizeRangeMake(CGSizeZero)]; + + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); + [node layoutIfNeeded]; + XCTAssertEqual(node.subnodes[0], node1); XCTAssertEqual(node.subnodes[1], node2); XCTAssertEqual(node.subnodes[2], node3); @@ -122,13 +126,14 @@ } }; - [node layoutThatFits:ASSizeRangeMake(CGSizeZero)]; + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); + [node layoutIfNeeded]; XCTAssertEqual(node.subnodes[0], node1); XCTAssertEqual(node.subnodes[1], node2); node.layoutState = @2; - [node invalidateCalculatedLayout]; - [node layoutThatFits:ASSizeRangeMake(CGSizeZero)]; + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); + [node layoutIfNeeded]; XCTAssertEqual(node.subnodes[0], node1); XCTAssertEqual(node.subnodes[1], node3); @@ -170,10 +175,12 @@ - (void)testMeasurementInBackgroundThreadWithLoadedNode { + const CGSize kNodeSize = CGSizeMake(100, 100); ASDisplayNode *node1 = [[ASDisplayNode alloc] init]; ASDisplayNode *node2 = [[ASDisplayNode alloc] init]; ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; + node.style.preferredSize = kNodeSize; node.automaticallyManagesSubnodes = YES; node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) { ASSpecTestDisplayNode *strongNode = (ASSpecTestDisplayNode *)weakNode; @@ -191,17 +198,34 @@ dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [node layoutThatFits:ASSizeRangeMake(CGSizeZero)]; - XCTAssertEqual(node.subnodes[0], node1); - - node.layoutState = @2; - [node invalidateCalculatedLayout]; - [node layoutThatFits:ASSizeRangeMake(CGSizeZero)]; + // Measurement happens in the background + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); // Dispatch back to the main thread to let the insertion / deletion of subnodes happening dispatch_async(dispatch_get_main_queue(), ^{ - XCTAssertEqual(node.subnodes[0], node2); - [expectation fulfill]; + + // Layout on main + [node layoutIfNeeded]; + XCTAssertEqual(node.subnodes[0], node1); + + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + // Change state and measure in the background + node.layoutState = @2; + [node setNeedsLayout]; + + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); + + // Dispatch back to the main thread to let the insertion / deletion of subnodes happening + dispatch_async(dispatch_get_main_queue(), ^{ + + // Layout on main again + [node layoutIfNeeded]; + XCTAssertEqual(node.subnodes[0], node2); + + [expectation fulfill]; + }); + }); }); }); @@ -214,12 +238,13 @@ - (void)testTransitionLayoutWithAnimationWithLoadedNodes { + const CGSize kNodeSize = CGSizeMake(100, 100); ASDisplayNode *node1 = [[ASDisplayNode alloc] init]; ASDisplayNode *node2 = [[ASDisplayNode alloc] init]; ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; node.automaticallyManagesSubnodes = YES; - + node.style.preferredSize = kNodeSize; node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) { ASSpecTestDisplayNode *strongNode = (ASSpecTestDisplayNode *)weakNode; if ([strongNode.layoutState isEqualToNumber:@1]) { @@ -236,7 +261,8 @@ XCTestExpectation *expectation = [self expectationWithDescription:@"Fix IHM layout transition also if one node is already loaded"]; - [node layoutThatFits:ASSizeRangeMake(CGSizeZero)]; + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); + [node layoutIfNeeded]; XCTAssertEqual(node.subnodes[0], node1); node.layoutState = @2; diff --git a/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m b/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m index 2c2286b00a..763237003d 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m @@ -28,8 +28,8 @@ node.layoutSpecBlock = ^(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5, 5, 5, 5) child:subnode]; }; - [node layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))]; + ASDisplayNodeSizeToFitSize(node, CGSizeMake(INFINITY, INFINITY)); ASSnapshotVerifyNode(node, nil); } diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 126e5c3806..c68e699381 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -2166,8 +2166,10 @@ static bool stringContainsPointer(NSString *description, id p) { // The inset spec here is crucial. If the nodes themselves are children, it passed before the fix. return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:underlay] overlay:overlay]; }; - [node layoutThatFits:ASSizeRangeMake(CGSizeMake(100, 100))]; - node.frame = (CGRect){ .size = node.calculatedSize }; + + ASDisplayNodeSizeToFitSize(node, CGSizeMake(100, 100)); + [node layoutIfNeeded]; + NSInteger underlayIndex = [node.subnodes indexOfObjectIdenticalTo:underlay]; NSInteger overlayIndex = [node.subnodes indexOfObjectIdenticalTo:overlay]; XCTAssertLessThan(underlayIndex, overlayIndex); @@ -2185,8 +2187,10 @@ static bool stringContainsPointer(NSString *description, id p) { // The inset spec here is crucial. If the nodes themselves are children, it passed before the fix. return [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:overlay background:[ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:underlay]]; }; - [node layoutThatFits:ASSizeRangeMake(CGSizeMake(100, 100))]; - node.frame = (CGRect){ .size = node.calculatedSize }; + + ASDisplayNodeSizeToFitSize(node, CGSizeMake(100, 100)); + [node layoutIfNeeded]; + NSInteger underlayIndex = [node.subnodes indexOfObjectIdenticalTo:underlay]; NSInteger overlayIndex = [node.subnodes indexOfObjectIdenticalTo:overlay]; XCTAssertLessThan(underlayIndex, overlayIndex); diff --git a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h b/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h index 7f1ad0b383..664bf30b77 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h +++ b/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h @@ -9,7 +9,13 @@ // #import +#import "ASDimension.h" + +@class ASDisplayNode; typedef BOOL (^as_condition_block_t)(void); BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block); + +void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size); +void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange); diff --git a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.m b/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.m index 728ffec66f..70f9502ac8 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.m @@ -9,6 +9,8 @@ // #import "ASDisplayNodeTestsHelper.h" +#import "ASDisplayNode.h" +#import "ASLayout.h" #import @@ -41,3 +43,12 @@ BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block) } return passed; } + +void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size) { + CGSize sizeThatFits = [node sizeThatFits:size]; + node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; +} +void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange) { + CGSize sizeThatFits = [node layoutThatFits:sizeRange].size; + node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; +} diff --git a/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m b/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m index 55706583a5..ac53e3cd4e 100644 --- a/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m @@ -30,7 +30,7 @@ // trivial test case to ensure ASSnapshotTestCase works ASImageNode *imageNode = [[ASImageNode alloc] init]; imageNode.image = [self testImage]; - [imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))]; + ASDisplayNodeSizeToFitSize(imageNode, CGSizeMake(100, 100)); ASSnapshotVerifyNode(imageNode, nil); } @@ -46,12 +46,14 @@ // Snapshot testing requires that node is formally laid out. imageNode.style.width = ASDimensionMake(forcedImageSize.width); imageNode.style.height = ASDimensionMake(forcedImageSize.height); - [imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, forcedImageSize)]; + ASDisplayNodeSizeToFitSize(imageNode, forcedImageSize); + [imageNode layoutIfNeeded]; ASSnapshotVerifyNode(imageNode, @"first"); imageNode.style.width = ASDimensionMake(200); imageNode.style.height = ASDimensionMake(200); - [imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(200, 200))]; + ASDisplayNodeSizeToFitSize(imageNode, CGSizeMake(200, 200)); + [imageNode layoutIfNeeded]; ASSnapshotVerifyNode(imageNode, @"second"); @@ -66,7 +68,7 @@ UIImage *tinted = ASImageNodeTintColorModificationBlock([UIColor redColor])(test); ASImageNode *node = [[ASImageNode alloc] init]; node.image = tinted; - [node layoutThatFits:ASSizeRangeMake(test.size)]; + ASDisplayNodeSizeToFitSize(node, test.size); ASSnapshotVerifyNode(node, nil); } @@ -81,7 +83,7 @@ UIImage *rounded = ASImageNodeRoundBorderModificationBlock(2, [UIColor redColor])(result); ASImageNode *node = [[ASImageNode alloc] init]; node.image = rounded; - [node layoutThatFits:ASSizeRangeMake(rounded.size)]; + ASDisplayNodeSizeToFitSize(node, rounded.size); ASSnapshotVerifyNode(node, nil); } diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m index e2d908d012..90a40f988c 100644 --- a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m +++ b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m @@ -39,7 +39,7 @@ node.layoutSpecUnderTest = layoutSpec; - [node layoutThatFits:sizeRange]; + ASDisplayNodeSizeToFitSizeRange(node, sizeRange); ASSnapshotVerifyNode(node, identifier); } @@ -56,6 +56,10 @@ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { + // As the layout spec under test can be used multiple times we set the isMutable to NO in here. This should only + // be done in tests and never in production codes + _layoutSpecUnderTest.isMutable = YES; + return _layoutSpecUnderTest; } diff --git a/AsyncDisplayKitTests/ASSnapshotTestCase.h b/AsyncDisplayKitTests/ASSnapshotTestCase.h index c89fc79af4..d3f6791d86 100644 --- a/AsyncDisplayKitTests/ASSnapshotTestCase.h +++ b/AsyncDisplayKitTests/ASSnapshotTestCase.h @@ -9,6 +9,7 @@ // #import +#import "ASDisplayNodeTestsHelper.h" @class ASDisplayNode; diff --git a/AsyncDisplayKitTests/ASSnapshotTestCase.m b/AsyncDisplayKitTests/ASSnapshotTestCase.m index 3bcfb67293..301ed923fe 100644 --- a/AsyncDisplayKitTests/ASSnapshotTestCase.m +++ b/AsyncDisplayKitTests/ASSnapshotTestCase.m @@ -37,8 +37,6 @@ NSOrderedSet *ASSnapshotTestCaseDefaultSuffixes(void) + (void)hackilySynchronouslyRecursivelyRenderNode:(ASDisplayNode *)node { - ASDisplayNodeAssertNotNil(node.calculatedLayout, @"Node %@ must be measured before it is rendered.", node); - node.bounds = (CGRect) { .size = node.calculatedSize }; ASDisplayNodePerformBlockOnEveryNode(nil, node, YES, ^(ASDisplayNode * _Nonnull node) { [node.layer setNeedsDisplay]; }); diff --git a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m index 811a967465..29d4bd4319 100644 --- a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m @@ -11,7 +11,6 @@ #import "ASSnapshotTestCase.h" #import -#import "ASLayout.h" @interface ASTextNodeSnapshotTests : ASSnapshotTestCase @@ -26,7 +25,8 @@ textNode.attributedText = [[NSAttributedString alloc] initWithString:@"judar" attributes:@{NSFontAttributeName : [UIFont italicSystemFontOfSize:24]}]; textNode.textContainerInset = UIEdgeInsetsMake(0, 2, 0, 2); - [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))]; + ASLayout *layout = [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))]; + textNode.frame = CGRectMake(0, 0, layout.size.width, layout.size.height); ASSnapshotVerifyNode(textNode, nil); } @@ -63,8 +63,8 @@ attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:30] }]; textNode.textContainerInset = UIEdgeInsetsMake(5, 10, 10, 5); - [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))]; - textNode.frame = CGRectMake(50, 50, textNode.calculatedSize.width, textNode.calculatedSize.height); + ASLayout *layout = [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY))]; + textNode.frame = CGRectMake(50, 50, layout.size.width, layout.size.height); [backgroundView addSubview:textNode.view]; backgroundView.frame = UIEdgeInsetsInsetRect(textNode.bounds, UIEdgeInsetsMake(-50, -50, -50, -50)); @@ -90,7 +90,7 @@ textNode.attributedText = [[NSAttributedString alloc] initWithString:@"Quality is Important" attributes:@{ NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont italicSystemFontOfSize:24] }]; // Set exclusion paths to trigger slow path textNode.exclusionPaths = @[ [UIBezierPath bezierPath] ]; - [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 50))]; + ASDisplayNodeSizeToFitSize(textNode, CGSizeMake(100, 50)); ASSnapshotVerifyNode(textNode, nil); } @@ -102,7 +102,7 @@ textNode.shadowOpacity = 0.3; textNode.shadowRadius = 3; textNode.shadowOffset = CGSizeMake(0, 1); - [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))]; + ASDisplayNodeSizeToFitSize(textNode, CGSizeMake(INFINITY, INFINITY)); ASSnapshotVerifyNode(textNode, nil); }