diff --git a/AsyncDisplayKit/ASCollectionNode.h b/AsyncDisplayKit/ASCollectionNode.h index 2e0c38cf97..bb3dfae87b 100644 --- a/AsyncDisplayKit/ASCollectionNode.h +++ b/AsyncDisplayKit/ASCollectionNode.h @@ -136,6 +136,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; +/** + * Scrolls the collection to the given item. + * + * @param indexPath The index path of the item. + * @param scrollPosition Where the item should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + * + * This method must be called on the main thread. + */ +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated; + #pragma mark - Editing /** diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index 9c7b226b81..a073769b08 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -335,6 +335,20 @@ [self.view deselectItemAtIndexPath:indexPath animated:animated]; } +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } else { + NSLog(@"Failed to scroll to item at index path %@ because the item never reached the view.", indexPath); + } +} + #pragma mark - Querying Data - (void)reloadDataInitiallyIfNeeded diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 35f1307a1a..33806b4c00 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -205,6 +205,15 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); +/** + * Scrolls the collection to the given item. + * + * @param indexPath The index path of the item. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + */ +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + /** * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. * The asyncDataSource must be updated to reflect the changes before the update block completes. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index adc9bc58ae..06aab6899b 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -555,19 +555,31 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - NSIndexPath *viewIndexPath = [self indexPathForNode:node]; - if (viewIndexPath == nil && wait) { - [self waitUntilAllUpdatesAreCommitted]; - viewIndexPath = [self indexPathForNode:node]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.item == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + NSIndexPath *viewIndexPath = [self indexPathForNode:node]; + if (viewIndexPath == nil && wait) { + [self waitUntilAllUpdatesAreCommitted]; + viewIndexPath = [self indexPathForNode:node]; + } + return viewIndexPath; } - return viewIndexPath; } - (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath { - ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; - return [_dataController indexPathForNode:node]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.item == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; + return [_dataController indexPathForNode:node]; + } } - (ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath @@ -596,23 +608,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return visibleNodes; } -/** - * TODO: This method was built when the distinction between data source - * index paths and view index paths was unclear. For compatibility, it - * still expects data source index paths for the time being. - */ -- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated -{ - ASDisplayNodeAssertMainThread(); - - NSIndexPath *viewIndexPath = [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; - if (viewIndexPath != nil) { - [super scrollToItemAtIndexPath:viewIndexPath atScrollPosition:scrollPosition animated:animated]; - } else { - NSLog(@"Warning: Ignoring request to scroll to item at index path %@ because the item did not reach the collection view.", indexPath); - } -} - /** * TODO: This method was built when the distinction between data source * index paths and view index paths was unclear. For compatibility, it diff --git a/AsyncDisplayKit/ASPagerNode.m b/AsyncDisplayKit/ASPagerNode.m index 0b42f2bc19..15a6ccb975 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/AsyncDisplayKit/ASPagerNode.m @@ -101,7 +101,7 @@ - (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; - [self.view scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:animated]; + [self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:animated]; } - (ASCellNode *)nodeForPageAtIndex:(NSInteger)index diff --git a/AsyncDisplayKit/ASTableNode.h b/AsyncDisplayKit/ASTableNode.h index 540f644aaf..83ae540cc7 100644 --- a/AsyncDisplayKit/ASTableNode.h +++ b/AsyncDisplayKit/ASTableNode.h @@ -104,6 +104,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; +/** + * Scrolls the table to the given row. + * + * @param indexPath The index path of the row. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + * + * This method must be called on the main thread. + */ +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; + /** * Reload everything from scratch, destroying the working range and all cached nodes. * diff --git a/AsyncDisplayKit/ASTableNode.mm b/AsyncDisplayKit/ASTableNode.mm index c2b8513ff1..954a2ae26e 100644 --- a/AsyncDisplayKit/ASTableNode.mm +++ b/AsyncDisplayKit/ASTableNode.mm @@ -359,6 +359,20 @@ ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock) [self.view deselectRowAtIndexPath:indexPath animated:animated]; } +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } else { + NSLog(@"Failed to scroll to row at index path %@ because the row never reached the view.", indexPath); + } +} + #pragma mark - Querying Data - (void)reloadDataInitiallyIfNeeded diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 301e94378b..2bed257651 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -124,6 +124,15 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); +/** + * Scrolls the table to the given row. + * + * @param indexPath The index path of the row. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + */ +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + /** * Similar to -visibleCells. * diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index fd879df740..e51a92cec1 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -464,14 +464,26 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - return [self indexPathForNode:node waitingIfNeeded:wait]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.row == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + return [self indexPathForNode:node waitingIfNeeded:wait]; + } } - (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath { - ASCellNode *node = [self nodeForRowAtIndexPath:indexPath]; - return [_dataController indexPathForNode:node]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.row == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [self nodeForRowAtIndexPath:indexPath]; + return [_dataController indexPathForNode:node]; + } } - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode @@ -528,25 +540,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_dataController waitUntilAllUpdatesAreCommitted]; } -/** - * TODO: This method was built when the distinction between data source - * index paths and view index paths was unclear. For compatibility, it - * still expects data source index paths for the time being. - * When the behavior is changed (to use the view index path directly) - * we should also remove the @c convertIndexPathFromTableNode: method. - */ -- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated -{ - ASDisplayNodeAssertMainThread(); - - indexPath = [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; - if (indexPath != nil) { - [super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; - } else { - NSLog(@"Warning: Ignoring request to scroll to row at index path %@ because the item did not reach the table view.", indexPath); - } -} - /** * TODO: This method was built when the distinction between data source * index paths and view index paths was unclear. For compatibility, it diff --git a/AsyncDisplayKit/ASTableViewInternal.h b/AsyncDisplayKit/ASTableViewInternal.h index d8f4afdcf2..0a9cbf63aa 100644 --- a/AsyncDisplayKit/ASTableViewInternal.h +++ b/AsyncDisplayKit/ASTableViewInternal.h @@ -37,4 +37,12 @@ /// Set YES and we'll log every time we call [super insertRows…] etc @property (nonatomic) BOOL test_enableSuperUpdateCallLogging; +/** + * Attempt to get the view-layer index path for the row with the given index path. + * + * @param indexPath The index path of the row. + * @param wait If the item hasn't reached the view yet, this attempts to wait for updates to commit. + */ +- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait; + @end diff --git a/AsyncDisplayKit/Details/ASCollectionInternal.h b/AsyncDisplayKit/Details/ASCollectionInternal.h index c76238ef70..1b4e7cc7a3 100644 --- a/AsyncDisplayKit/Details/ASCollectionInternal.h +++ b/AsyncDisplayKit/Details/ASCollectionInternal.h @@ -25,6 +25,15 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode; @property (nonatomic, strong, readonly) ASDataController *dataController; @property (nonatomic, strong, readonly) ASRangeController *rangeController; + +/** + * Attempt to get the view-layer index path for the item with the given index path. + * + * @param indexPath The index path of the item. + * @param wait If the item hasn't reached the view yet, this attempts to wait for updates to commit. + */ +- (nullable NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait; + @end NS_ASSUME_NONNULL_END