diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4cfc03270d..b0e7a94bb5 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -564,13 +564,30 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - [_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { - ASSizeRange constrainedSize = [_dataSource dataController:self - constrainedSizeForNodeAtIndexPath:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { + if (!nodes.count) { + return; + } + + [self accessDataSourceWithBlock:^{ + [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + }]; + }]; }]; + }; + + // Can't relayout right away because _completedNodes may not be up-to-date, + // i.e there might be some nodes that were measured using the old constrained size but haven't been added to _completedNodes + // (see _layoutNodes:atIndexPaths:withAnimationOptions:). + [_editingTransactionQueue addOperationWithBlock:^{ + ASDisplayNodePerformBlockOnMainThread(^{ + relayoutNodesBlock(_completedNodes); + }); }]; }]; } diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index e73ff71e33..4a23a65916 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -202,4 +202,44 @@ } } +- (void)testRelayoutAllRows +{ + // Initial width of the table view is 0 and all nodes are measured with this size. + ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, 0, 500) + style:UITableViewStylePlain + asyncDataFetching:YES]; + CGSize tableViewFinalSize = CGSizeMake(100, 500); + + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + + tableView.asyncDelegate = dataSource; + tableView.asyncDataSource = dataSource; + + [tableView reloadData]; + + [tableView beginUpdates]; + + tableView.frame = CGRectMake(0, 0, tableViewFinalSize.width, tableViewFinalSize.height); + [tableView layoutIfNeeded]; + + XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; + + [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASCellNode *node = [tableView nodeForRowAtIndexPath:indexPath]; + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewFinalSize.width); + } + } + [nodesMeasuredUsingNewConstrainedSizeExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; +} + @end