[ASCellNode] Added indexPath property. (#2468)

* [ASCellNode] Added indexPath property.

* [ASCellNode] Cached the type of scrollView we're using in the node, and placed that logic in setScrollView.

* [ASCellNode] Removed table and collection view from indexPath, since they return the index paths from the view-space.

* Changed the logic for getting indexPath so that it works even when the cell is not displayed.

* [VerticalWithinHorizontalScrolling] Explicitally synthesized indexPath property.
This commit is contained in:
george-gw
2016-10-28 01:56:00 +02:00
committed by Adlai Holler
parent 4e32cf6388
commit 54cda5fdbb
9 changed files with 106 additions and 18 deletions

View File

@@ -63,6 +63,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy, nullable) NSIndexPath *cachedIndexPath; @property (nonatomic, copy, nullable) NSIndexPath *cachedIndexPath;
@property (weak, nonatomic, nullable) ASDisplayNode *owningNode;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -108,6 +108,20 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {
*/ */
@property (nonatomic, assign, getter=isHighlighted) BOOL highlighted; @property (nonatomic, assign, getter=isHighlighted) BOOL highlighted;
/**
* The current index path of this cell node, or @c nil if this node is
* not a valid item inside a table node or collection node.
*
* @note This property must be accessed on the main thread.
*/
@property (nonatomic, readonly, nullable) NSIndexPath *indexPath;
/**
* The owning node (ASCollectionNode/ASTableNode) of this cell node, or @c nil if this node is
* not a valid item inside a table node or collection node or if those nodes are nil.
*/
@property (weak, nonatomic, readonly, nullable) ASDisplayNode *owningNode;
/* /*
* ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding * ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding
* these methods (e.g. for highlighting) requires the super method be called. * these methods (e.g. for highlighting) requires the super method be called.

View File

@@ -36,6 +36,12 @@
ASDisplayNode *_viewControllerNode; ASDisplayNode *_viewControllerNode;
UIViewController *_viewController; UIViewController *_viewController;
BOOL _suspendInteractionDelegate; BOOL _suspendInteractionDelegate;
struct {
unsigned int isTableNode:1;
unsigned int isCollectionNode:1;
} _owningNodeType;
} }
@end @end
@@ -186,6 +192,19 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
} }
} }
- (void)setOwningNode:(ASDisplayNode *)owningNode
{
_owningNode = owningNode;
memset(&_owningNodeType, 0, sizeof(_owningNodeType));
if ([owningNode isKindOfClass:[ASTableNode class]]) {
_owningNodeType.isTableNode = 1;
} else if ([owningNode isKindOfClass:[ASCollectionNode class]]) {
_owningNodeType.isCollectionNode = 1;
}
}
- (void)__setSelectedFromUIKit:(BOOL)selected; - (void)__setSelectedFromUIKit:(BOOL)selected;
{ {
if (selected != _selected) { if (selected != _selected) {
@@ -204,6 +223,19 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
} }
} }
- (NSIndexPath *)indexPath
{
ASDisplayNodeAssertMainThread();
if (_owningNodeType.isTableNode) {
return [(ASTableNode *)self.owningNode indexPathForNode:self];
} else if (_owningNodeType.isCollectionNode) {
return [(ASCollectionNode *)self.owningNode indexPathForNode:self];
}
return nil;
}
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" #pragma clang diagnostic ignored "-Wobjc-missing-super-calls"

View File

@@ -233,15 +233,14 @@
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
{ {
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment]; __weak id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
[sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { [sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
for (NSUInteger sec = range.location; sec < NSMaxRange(range); sec++) { for (NSUInteger sec = range.location; sec < NSMaxRange(range); sec++) {
NSUInteger itemCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:sec]; NSUInteger itemCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:sec];
for (NSUInteger i = 0; i < itemCount; i++) { for (NSUInteger i = 0; i < itemCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec]; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec];
[self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection]; [self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environment:environment];
} }
} }
}]; }];
@@ -249,8 +248,7 @@
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts - (void)_populateSupplementaryNodesOfKind:(NSString *)kind atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
{ {
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment]; __weak id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
NSMutableIndexSet *sections = [NSMutableIndexSet indexSet]; NSMutableIndexSet *sections = [NSMutableIndexSet indexSet];
for (NSIndexPath *indexPath in indexPaths) { for (NSIndexPath *indexPath in indexPaths) {
@@ -262,13 +260,13 @@
NSUInteger itemCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:sec]; NSUInteger itemCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:sec];
for (NSUInteger i = 0; i < itemCount; i++) { for (NSUInteger i = 0; i < itemCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec]; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec];
[self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection]; [self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environment:environment];
} }
} }
}]; }];
} }
- (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection - (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts environment:(id<ASEnvironment>)environment
{ {
ASCellNodeBlock supplementaryCellBlock; ASCellNodeBlock supplementaryCellBlock;
if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) { if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) {
@@ -283,7 +281,7 @@
indexPath:indexPath indexPath:indexPath
supplementaryElementKind:kind supplementaryElementKind:kind
constrainedSize:constrainedSize constrainedSize:constrainedSize
environmentTraitCollection:environmentTraitCollection]; environment:environment];
[contexts addObject:context]; [contexts addObject:context];
} }

View File

@@ -476,8 +476,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment]; __weak id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
std::vector<NSInteger> counts = [self itemCountsFromDataSource]; std::vector<NSInteger> counts = [self itemCountsFromDataSource];
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array]; NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
@@ -493,7 +492,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
indexPath:indexPath indexPath:indexPath
supplementaryElementKind:nil supplementaryElementKind:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
environmentTraitCollection:environmentTraitCollection]]; environment:environment]];
} }
} }
}]; }];
@@ -743,8 +742,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
NSMutableArray<ASIndexedNodeContext *> *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; NSMutableArray<ASIndexedNodeContext *> *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment]; __weak id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
for (NSIndexPath *indexPath in sortedIndexPaths) { for (NSIndexPath *indexPath in sortedIndexPaths) {
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath]; ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
@@ -753,7 +751,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
indexPath:indexPath indexPath:indexPath
supplementaryElementKind:nil supplementaryElementKind:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
environmentTraitCollection:environmentTraitCollection]]; environment:environment]];
} }
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_nodeContexts[ASDataControllerRowNodeKind], sortedIndexPaths, contexts); ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_nodeContexts[ASDataControllerRowNodeKind], sortedIndexPaths, contexts);

View File

@@ -24,13 +24,14 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly, strong) NSIndexPath *indexPath; @property (nonatomic, readonly, strong) NSIndexPath *indexPath;
@property (nonatomic, readonly, copy, nullable) NSString *supplementaryElementKind; @property (nonatomic, readonly, copy, nullable) NSString *supplementaryElementKind;
@property (nonatomic, readonly, assign) ASSizeRange constrainedSize; @property (nonatomic, readonly, assign) ASSizeRange constrainedSize;
@property (weak, nonatomic) id<ASEnvironment> environment;
@property (nonatomic, readonly, assign) ASEnvironmentTraitCollection environmentTraitCollection; @property (nonatomic, readonly, assign) ASEnvironmentTraitCollection environmentTraitCollection;
- (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock - (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock
indexPath:(NSIndexPath *)indexPath indexPath:(NSIndexPath *)indexPath
supplementaryElementKind:(nullable NSString *)supplementaryElementKind supplementaryElementKind:(nullable NSString *)supplementaryElementKind
constrainedSize:(ASSizeRange)constrainedSize constrainedSize:(ASSizeRange)constrainedSize
environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection; environment:(id<ASEnvironment>)environment;
/** /**
* @return The node, running the node block if necessary. The node block will be discarded * @return The node, running the node block if necessary. The node block will be discarded

View File

@@ -31,7 +31,7 @@
indexPath:(NSIndexPath *)indexPath indexPath:(NSIndexPath *)indexPath
supplementaryElementKind:(nullable NSString *)supplementaryElementKind supplementaryElementKind:(nullable NSString *)supplementaryElementKind
constrainedSize:(ASSizeRange)constrainedSize constrainedSize:(ASSizeRange)constrainedSize
environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection environment:(id<ASEnvironment>)environment
{ {
NSAssert(nodeBlock != nil && indexPath != nil, @"Node block and index path must not be nil"); NSAssert(nodeBlock != nil && indexPath != nil, @"Node block and index path must not be nil");
self = [super init]; self = [super init];
@@ -40,7 +40,8 @@
_indexPath = indexPath; _indexPath = indexPath;
_supplementaryElementKind = [supplementaryElementKind copy]; _supplementaryElementKind = [supplementaryElementKind copy];
_constrainedSize = constrainedSize; _constrainedSize = constrainedSize;
_environmentTraitCollection = environmentTraitCollection; _environment = environment;
_environmentTraitCollection = environment.environmentTraitCollection;
} }
return self; return self;
} }
@@ -57,6 +58,7 @@
} }
node.cachedIndexPath = _indexPath; node.cachedIndexPath = _indexPath;
node.supplementaryElementKind = _supplementaryElementKind; node.supplementaryElementKind = _supplementaryElementKind;
node.owningNode = (ASDisplayNode *)_environment;
ASEnvironmentStatePropagateDown(node, _environmentTraitCollection); ASEnvironmentStatePropagateDown(node, _environmentTraitCollection);
_node = node; _node = node;
} }

View File

@@ -504,6 +504,45 @@
} }
} }
- (void)testCellNodeIndexPathConsistency
{
updateValidationTestPrologue
// Test with a visible cell
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:2 inSection:0];
ASCellNode *cell = [cn nodeForItemAtIndexPath:indexPath];
// Check if cell's indexPath corresponds to the indexPath being tested
XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == indexPath.item, @"Expected the cell's indexPath to be the same as the indexPath being tested.");
// Remove an item prior to the cell's indexPath from the same section and check for indexPath consistency
--del->_itemCounts[indexPath.section];
[cn deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:indexPath.section]]];
XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == (indexPath.item - 1), @"Expected the cell's indexPath to be updated once a cell with a lower index is deleted.");
// Remove the section that includes the indexPath and check if the cell's indexPath is now nil
del->_itemCounts.erase(del->_itemCounts.begin());
[cn deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]];
XCTAssertNil(cell.indexPath, @"Expected the cell's indexPath to be nil once the section that contains the node is deleted.");
// Run the same tests but with a non-displayed cell
indexPath = [NSIndexPath indexPathForItem:2 inSection:(del->_itemCounts.size() - 1)];
cell = [cn nodeForItemAtIndexPath:indexPath];
// Check if cell's indexPath corresponds to the indexPath being tested
XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == indexPath.item, @"Expected the cell's indexPath to be the same as the indexPath in question.");
// Remove an item prior to the cell's indexPath from the same section and check for indexPath consistency
--del->_itemCounts[indexPath.section];
[cn deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:indexPath.section]]];
XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == (indexPath.item - 1), @"Expected the cell's indexPath to be updated once a cell with a lower index is deleted.");
// Remove the section that includes the indexPath and check if the cell's indexPath is now nil
del->_itemCounts.pop_back();
[cn deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]];
XCTAssertNil(cell.indexPath, @"Expected the cell's indexPath to be nil once the section that contains the node is deleted.");
}
/** /**
* https://github.com/facebook/AsyncDisplayKit/issues/2011 * https://github.com/facebook/AsyncDisplayKit/issues/2011
* *

View File

@@ -22,6 +22,8 @@
@implementation RandomCoreGraphicsNode @implementation RandomCoreGraphicsNode
@synthesize indexPath=_indexPath;
+ (UIColor *)randomColor + (UIColor *)randomColor
{ {
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0