mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-05-12 08:05:36 +08:00
Merge in downstream changes.
Introduce `ASTableView`, a UITableView subclass that uses `ASCellNode` instead of UITableViewCell. Add working range support via `ASRangeController`, which observes the visible range, maintains a working range, and handles most ASDK machinery. ASRangeController is loosely-enough coupled that it should be easily adapted to UICollectionView if that's desired in the future. Notable considerations in the ASRangeController architecture: * There's no sense rewriting UITableView -- the real win comes from using nodes instead of UITableViewCells (easily parallelisable computation, large number of cells vs. few table views, etc.). So, use a UITableView with empty cells, using UITableViewCell's contentView as a host for arbitrary node hierarchies. * Instead of lazy-loading cells the instant they're needed by UITableView, load them in advance. Preload a substantial number of nodes in the direction of scroll, as well as a small buffer in the other direction. * Maintain compatibility with UITableView's API, with one primary change -- consumer code yields configured ASCellNodes, not UITableViewCells. * Don't use -tableView:heightForRowAtIndexPath:. Nodes already compute their preferred sizes and cache results for use at layout-time, so ASTableView uses their calculatedSizes directly. * Corollary: ASTableView is only aware of nodes that have been sized. This means that, if a cell appears onscreen, it has layout data and can display a "realistic placeholder", e.g. by making its subnodes' background colour grey. Other improvements: * Remove dead references and update headers (fixes #7, #20). * Rename `-[ASDisplayNode sizeToFit:]` to `-measure:` and fix `constrainedSizeForCalulatedSize` typo (fixes #15). * Rename `-willAppear` and `-didDisappear` to `-willEnterHierarchy` and `-didExitHierarchy`. Remove `-willDisappear` -- it was redundant, and there was no counterpart `-didAppear`. * Rename `viewLoaded` to `nodeLoaded`.
This commit is contained in:
@@ -64,9 +64,8 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
|
||||
NSMutableArray *_swizzleCleanupBlocks;
|
||||
|
||||
NSCountedSet *_willAppearCounts;
|
||||
NSCountedSet *_willDisappearCounts;
|
||||
NSCountedSet *_didDisappearCounts;
|
||||
NSCountedSet *_willEnterHierarchyCounts;
|
||||
NSCountedSet *_didExitHierarchyCounts;
|
||||
|
||||
}
|
||||
|
||||
@@ -77,20 +76,15 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
_swizzleCleanupBlocks = [[NSMutableArray alloc] init];
|
||||
|
||||
// Using this instead of mocks. Count # of times method called
|
||||
_willAppearCounts = [[NSCountedSet alloc] init];
|
||||
_willDisappearCounts = [[NSCountedSet alloc] init];
|
||||
_didDisappearCounts = [[NSCountedSet alloc] init];
|
||||
_willEnterHierarchyCounts = [[NSCountedSet alloc] init];
|
||||
_didExitHierarchyCounts = [[NSCountedSet alloc] init];
|
||||
|
||||
dispatch_block_t cleanupBlock = modifyMethodByAddingPrologueBlockAndReturnCleanupBlock([ASDisplayNode class], @selector(willAppear), ^(id blockSelf){
|
||||
[_willAppearCounts addObject:blockSelf];
|
||||
dispatch_block_t cleanupBlock = modifyMethodByAddingPrologueBlockAndReturnCleanupBlock([ASDisplayNode class], @selector(willEnterHierarchy), ^(id blockSelf){
|
||||
[_willEnterHierarchyCounts addObject:blockSelf];
|
||||
});
|
||||
[_swizzleCleanupBlocks addObject:cleanupBlock];
|
||||
cleanupBlock = modifyMethodByAddingPrologueBlockAndReturnCleanupBlock([ASDisplayNode class], @selector(didDisappear), ^(id blockSelf){
|
||||
[_didDisappearCounts addObject:blockSelf];
|
||||
});
|
||||
[_swizzleCleanupBlocks addObject:cleanupBlock];
|
||||
cleanupBlock = modifyMethodByAddingPrologueBlockAndReturnCleanupBlock([ASDisplayNode class], @selector(willDisappear), ^(id blockSelf){
|
||||
[_willDisappearCounts addObject:blockSelf];
|
||||
cleanupBlock = modifyMethodByAddingPrologueBlockAndReturnCleanupBlock([ASDisplayNode class], @selector(didExitHierarchy), ^(id blockSelf){
|
||||
[_didExitHierarchyCounts addObject:blockSelf];
|
||||
});
|
||||
[_swizzleCleanupBlocks addObject:cleanupBlock];
|
||||
}
|
||||
@@ -106,12 +100,10 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
[_swizzleCleanupBlocks release];
|
||||
_swizzleCleanupBlocks = nil;
|
||||
|
||||
[_willAppearCounts release];
|
||||
_willAppearCounts = nil;
|
||||
[_willDisappearCounts release];
|
||||
_willDisappearCounts = nil;
|
||||
[_didDisappearCounts release];
|
||||
_didDisappearCounts = nil;
|
||||
[_willEnterHierarchyCounts release];
|
||||
_willEnterHierarchyCounts = nil;
|
||||
[_didExitHierarchyCounts release];
|
||||
_didExitHierarchyCounts = nil;
|
||||
}
|
||||
|
||||
- (void)testAppearanceMethodsCalledWithRootNodeInWindowLayer
|
||||
@@ -140,14 +132,12 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
[superview addSubview:n.view];
|
||||
}
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:n], 0u, @"willAppear erroneously called");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:n], 0u, @"willDisppear erroneously called");
|
||||
XCTAssertEqual([_didDisappearCounts countForObject:n], 0u, @"didDisappear erroneously called");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:n], 0u, @"willEnterHierarchy erroneously called");
|
||||
XCTAssertEqual([_didExitHierarchyCounts countForObject:n], 0u, @"didExitHierarchy erroneously called");
|
||||
|
||||
[window addSubview:superview];
|
||||
XCTAssertEqual([_willAppearCounts countForObject:n], 1u, @"willAppear not called when node's view added to hierarchy");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:n], 0u, @"willDisppear erroneously called");
|
||||
XCTAssertEqual([_didDisappearCounts countForObject:n], 0u, @"didDisappear erroneously called");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:n], 1u, @"willEnterHierarchy not called when node's view added to hierarchy");
|
||||
XCTAssertEqual([_didExitHierarchyCounts countForObject:n], 0u, @"didExitHierarchy erroneously called");
|
||||
|
||||
XCTAssertTrue(n.inWindow, @"Node should be visible");
|
||||
|
||||
@@ -159,9 +149,8 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
|
||||
XCTAssertFalse(n.inWindow, @"Node should be not visible");
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:n], 1u, @"willAppear not called when node's view added to hierarchy");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:n], 1u, @"willDisppear erroneously called");
|
||||
XCTAssertEqual([_didDisappearCounts countForObject:n], 1u, @"didDisappear erroneously called");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:n], 1u, @"willEnterHierarchy not called when node's view added to hierarchy");
|
||||
XCTAssertEqual([_didExitHierarchyCounts countForObject:n], 1u, @"didExitHierarchy erroneously called");
|
||||
|
||||
[superview release];
|
||||
[window release];
|
||||
@@ -198,11 +187,11 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
[window addSubview:parent.view];
|
||||
}
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:parent], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:a], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:b], 0u, @"Should not have appeared yet");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:aa], 0u, @"Should not have appeared yet");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:ab], 0u, @"Should not have appeared yet");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:parent], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:a], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:b], 0u, @"Should not have appeared yet");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:aa], 0u, @"Should not have appeared yet");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:ab], 0u, @"Should not have appeared yet");
|
||||
|
||||
XCTAssertTrue(parent.inWindow, @"Should be visible");
|
||||
XCTAssertTrue(a.inWindow, @"Should be visible");
|
||||
@@ -221,11 +210,11 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
XCTAssertTrue(aa.inWindow, @"Nothing should be visible");
|
||||
XCTAssertTrue(ab.inWindow, @"Nothing should be visible");
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:parent], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:a], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:b], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:aa], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:ab], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:parent], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:a], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:b], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:aa], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:ab], 1u, @"Should have -willEnterHierarchy called once");
|
||||
|
||||
if (isLayerBacked) {
|
||||
[parent.layer removeFromSuperlayer];
|
||||
@@ -233,12 +222,6 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
[parent.view removeFromSuperview];
|
||||
}
|
||||
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:parent], 1u, @"Should disappear properly");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:a], 1u, @"Should disappear properly");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:b], 1u, @"Should disappear properly");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:aa], 1u, @"Should disappear properly");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:ab], 1u, @"Should disappear properly");
|
||||
|
||||
XCTAssertFalse(parent.inWindow, @"Nothing should be visible");
|
||||
XCTAssertFalse(a.inWindow, @"Nothing should be visible");
|
||||
XCTAssertFalse(b.inWindow, @"Nothing should be visible");
|
||||
@@ -338,8 +321,8 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
XCTAssertFalse(childSubnode.inWindow, @"Should not yet be visible");
|
||||
XCTAssertFalse(childSubnode.inWindow, @"Should not yet be visible");
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:child], 0u, @"Should not have -willAppear called");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:childSubnode], 0u, @"Should not have -willAppear called");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:child], 0u, @"Should not have -willEnterHierarchy called");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:childSubnode], 0u, @"Should not have -willEnterHierarchy called");
|
||||
|
||||
if (isLayerBacked) {
|
||||
[window.layer addSublayer:parentA.layer];
|
||||
@@ -354,8 +337,8 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
XCTAssertTrue(child.inWindow, @"Should be visible after parent added to window");
|
||||
XCTAssertTrue(childSubnode.inWindow, @"Should be visible after parent added to window");
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:child], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willAppearCounts countForObject:childSubnode], 1u, @"Should have -willAppear called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:child], 1u, @"Should have -willEnterHierarchy called once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:childSubnode], 1u, @"Should have -willEnterHierarchy called once");
|
||||
|
||||
// Move subnode from A to B
|
||||
if (useManualDisable) {
|
||||
@@ -372,9 +355,7 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
ASDisplayNodeEnableHierarchyNotifications(child);
|
||||
}
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:child], 1u, @"Should not have -willAppear called when moving child around in hierarchy");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:child], 0u, @"Should not have -willDisappear called when moving child around in hierarchy");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:childSubnode], 0u, @"Subnode should not have -willDisappear called when moving parent (child) around in hierarchy");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:child], 1u, @"Should not have -willEnterHierarchy called when moving child around in hierarchy");
|
||||
|
||||
// Move subnode back to A
|
||||
if (useManualDisable) {
|
||||
@@ -392,15 +373,12 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
}
|
||||
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:child], 1u, @"Should not have -willAppear called when moving child around in hierarchy");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:child], 0u, @"Should not have -willDisappear called when moving child around in hierarchy");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:child], 1u, @"Should not have -willEnterHierarchy called when moving child around in hierarchy");
|
||||
|
||||
// Finally, remove subnode
|
||||
[child removeFromSupernode];
|
||||
|
||||
XCTAssertEqual([_willAppearCounts countForObject:child], 1u, @"Should appear and disappear just once");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:child], 1u, @"Should appear and disappear just once");
|
||||
XCTAssertEqual([_willDisappearCounts countForObject:childSubnode], 1u, @"Should appear and disappear just once");
|
||||
XCTAssertEqual([_willEnterHierarchyCounts countForObject:child], 1u, @"Should appear and disappear just once");
|
||||
|
||||
// Make sure that we don't leave these unbalanced
|
||||
XCTAssertFalse([child __visibilityNotificationsDisabled], @"Unbalanced visibility notifications calls");
|
||||
@@ -442,9 +420,8 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
|
||||
{
|
||||
DeclareNodeNamed(n);
|
||||
|
||||
XCTAssertThrows([n willAppear], @"Should not allow manually calling appearance methods.");
|
||||
XCTAssertThrows([n willDisappear], @"Should not allow manually calling appearance methods.");
|
||||
XCTAssertThrows([n didDisappear], @"Should not allow manually calling appearance methods.");
|
||||
XCTAssertThrows([n willEnterHierarchy], @"Should not allow manually calling appearance methods.");
|
||||
XCTAssertThrows([n didExitHierarchy], @"Should not allow manually calling appearance methods.");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user