[Umbrella] ASCollectionView -> ASCollectionNode Migration, Separate Index Spaces (#2372)

* Separate dataSource & UIKit index spaces

Beef up our supplementary node support

Make the API way better

Go nuts

Add a unit test for UICollectionView's handling of reloadData inside batch updates

Wrap indexPathForNode: in a cache

Convert index paths in delegate methods

Go back on table view

Put collection view back

Switch up the API

Move most ASCollectionView API to ASCollectionNode

Move most table logic over to ASTableNode

Do the things

More conversion work

Keep on keepin' on

Get table view delegate API done

More porting

Simplify

Clear the delegate

More cleanup

Move more stuff around

Remove pointless file

Re-add some API

Put back more API

Use the right flag

* Some cleanup

* Remove incorrect comment

* Tweak the API

* Put back a couple methods

* update example projects (note: ASCollectionView deprecation warnings expected)

* change reloadDataWithCompletion:nil --> reloadData

* Clean up rebase

* Make deprecated numberOfItemsInSection methods optional

* Use the right flag

* Address nits

* update ASDKTube, ASDKgram & ASViewController examples
This commit is contained in:
Adlai Holler
2016-10-14 17:21:16 -07:00
committed by GitHub
parent fb768683bc
commit c2ea7cfeac
50 changed files with 3448 additions and 1122 deletions

View File

@@ -23,6 +23,7 @@
#import "_ASDisplayLayer.h"
#import "ASTableNode.h"
#import "ASEqualityHelpers.h"
#import "ASTableView+Undeprecated.h"
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
@@ -130,36 +131,61 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
NSMutableSet *_cellsForVisibilityUpdates;
struct {
unsigned int asyncDelegateScrollViewDidScroll:1;
unsigned int asyncDelegateScrollViewWillBeginDragging:1;
unsigned int asyncDelegateScrollViewDidEndDragging:1;
unsigned int asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath:1;
unsigned int asyncDelegateTableViewWillDisplayNodeForRowAtIndexPathDeprecated:1;
unsigned int asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath:1;
unsigned int asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset:1;
unsigned int asyncDelegateTableViewWillBeginBatchFetchWithContext:1;
unsigned int asyncDelegateShouldBatchFetchForTableView:1;
unsigned int asyncDelegateTableViewConstrainedSizeForRowAtIndexPath:1;
unsigned int scrollViewDidScroll:1;
unsigned int scrollViewWillBeginDragging:1;
unsigned int scrollViewDidEndDragging:1;
unsigned int scrollViewWillEndDragging:1;
unsigned int tableNodeWillDisplayNodeForRow:1;
unsigned int tableViewWillDisplayNodeForRow:1;
unsigned int tableViewWillDisplayNodeForRowDeprecated:1;
unsigned int tableNodeDidEndDisplayingNodeForRow:1;
unsigned int tableViewDidEndDisplayingNodeForRow:1;
unsigned int tableNodeWillBeginBatchFetch:1;
unsigned int tableViewWillBeginBatchFetch:1;
unsigned int shouldBatchFetchForTableView:1;
unsigned int shouldBatchFetchForTableNode:1;
unsigned int tableViewConstrainedSizeForRow:1;
unsigned int tableNodeConstrainedSizeForRow:1;
unsigned int tableViewWillSelectRow:1;
unsigned int tableNodeWillSelectRow:1;
unsigned int tableViewDidSelectRow:1;
unsigned int tableNodeDidSelectRow:1;
unsigned int tableViewWillDeselectRow:1;
unsigned int tableNodeWillDeselectRow:1;
unsigned int tableViewDidDeselectRow:1;
unsigned int tableNodeDidDeselectRow:1;
unsigned int tableViewShouldHighlightRow:1;
unsigned int tableNodeShouldHighlightRow:1;
unsigned int tableViewDidHighlightRow:1;
unsigned int tableNodeDidHighlightRow:1;
unsigned int tableViewDidUnhighlightRow:1;
unsigned int tableNodeDidUnhighlightRow:1;
unsigned int tableViewShouldShowMenuForRow:1;
unsigned int tableNodeShouldShowMenuForRow:1;
unsigned int tableViewCanPerformActionForRow:1;
unsigned int tableNodeCanPerformActionForRow:1;
unsigned int tableViewPerformActionForRow:1;
unsigned int tableNodePerformActionForRow:1;
} _asyncDelegateFlags;
struct {
unsigned int asyncDataSourceNumberOfSectionsInTableView:1;
unsigned int asyncDataSourceTableViewNodeBlockForRowAtIndexPath:1;
unsigned int asyncDataSourceTableViewNodeForRowAtIndexPath:1;
unsigned int asyncDataSourceTableViewCanMoveRowAtIndexPath:1;
unsigned int asyncDataSourceTableViewMoveRowAtIndexPath:1;
unsigned int numberOfSectionsInTableView:1;
unsigned int numberOfSectionsInTableNode:1;
unsigned int tableNodeNumberOfRowsInSection:1;
unsigned int tableViewNumberOfRowsInSection:1;
unsigned int tableViewNodeBlockForRow:1;
unsigned int tableNodeNodeBlockForRow:1;
unsigned int tableViewNodeForRow:1;
unsigned int tableNodeNodeForRow:1;
unsigned int tableViewCanMoveRow:1;
unsigned int tableNodeCanMoveRow:1;
unsigned int tableViewMoveRow:1;
unsigned int tableNodeMoveRow:1;
} _asyncDataSourceFlags;
}
@property (nonatomic, strong, readwrite) ASDataController *dataController;
// Used only when ASTableView is created directly rather than through ASTableNode.
// We create a node so that logic related to appearance, memory management, etc can be located there
// for both the node-based and view-based version of the table.
// This also permits sharing logic with ASCollectionNode, as the superclass is not UIKit-controlled.
@property (nonatomic, strong) ASTableNode *strongTableNode;
// Always set, whether ASCollectionView is created directly or via ASCollectionNode.
@property (nonatomic, weak) ASTableNode *tableNode;
@property (nonatomic) BOOL test_enableSuperUpdateCallLogging;
@@ -217,10 +243,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
return [self _initWithFrame:frame style:style dataControllerClass:nil ownedByNode:NO];
return [self _initWithFrame:frame style:style dataControllerClass:nil];
}
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass ownedByNode:(BOOL)ownedByNode
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
{
if (!(self = [super initWithFrame:frame style:style])) {
return nil;
@@ -232,15 +258,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
[self configureWithDataControllerClass:dataControllerClass];
if (!ownedByNode) {
// See commentary at the definition of .strongTableNode for why we create an ASTableNode.
// FIXME: The _view pointer of the node retains us, but the node will die immediately if we don't
// retain it. At the moment there isn't a great solution to this, so we can't yet move our core
// logic to ASTableNode (required to have a shared superclass with ASCollection*).
ASTableNode *tableNode = nil; //[[ASTableNode alloc] _initWithTableView:self];
self.strongTableNode = tableNode;
}
if (!AS_AT_LEAST_IOS9) {
_retainedLayer = self.layer;
}
@@ -277,7 +294,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
ASDisplayNodeAssert(delegate == nil, @"ASTableView uses asyncDelegate, not UITableView's delegate property.");
}
- (void)setAsyncDataSource:(id<ASTableViewDataSource>)asyncDataSource
- (void)setAsyncDataSource:(id<ASTableDataSource>)asyncDataSource
{
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
@@ -294,20 +311,28 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
_asyncDataSource = asyncDataSource;
_proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
_asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)];
_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)];
_asyncDataSourceFlags.asyncDataSourceTableViewCanMoveRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:canMoveRowAtIndexPath:)];
_asyncDataSourceFlags.asyncDataSourceTableViewMoveRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:moveRowAtIndexPath:toIndexPath:)];
_asyncDataSourceFlags.numberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
_asyncDataSourceFlags.numberOfSectionsInTableNode = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableNode:)];
_asyncDataSourceFlags.tableViewNumberOfRowsInSection = [_asyncDataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)];
_asyncDataSourceFlags.tableNodeNumberOfRowsInSection = [_asyncDataSource respondsToSelector:@selector(tableNode:numberOfRowsInSection:)];
_asyncDataSourceFlags.tableViewNodeForRow = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)];
_asyncDataSourceFlags.tableNodeNodeForRow = [_asyncDataSource respondsToSelector:@selector(tableNode:nodeForRowAtIndexPath:)];
_asyncDataSourceFlags.tableViewNodeBlockForRow = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)];
_asyncDataSourceFlags.tableNodeNodeBlockForRow = [_asyncDataSource respondsToSelector:@selector(tableNode:nodeBlockForRowAtIndexPath:)];
_asyncDataSourceFlags.tableViewCanMoveRow = [_asyncDataSource respondsToSelector:@selector(tableView:canMoveRowAtIndexPath:)];
_asyncDataSourceFlags.tableViewMoveRow = [_asyncDataSource respondsToSelector:@selector(tableView:moveRowAtIndexPath:toIndexPath:)];
ASDisplayNodeAssert(_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath
|| _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath, @"Data source must implement tableView:nodeBlockForRowAtIndexPath: or tableView:nodeForRowAtIndexPath:");
ASDisplayNodeAssert(_asyncDataSourceFlags.tableViewNodeBlockForRow
|| _asyncDataSourceFlags.tableViewNodeForRow
|| _asyncDataSourceFlags.tableNodeNodeBlockForRow
|| _asyncDataSourceFlags.tableNodeNodeForRow, @"Data source must implement tableNode:nodeBlockForRowAtIndexPath: or tableNode:nodeForRowAtIndexPath:");
ASDisplayNodeAssert(_asyncDataSourceFlags.tableNodeNumberOfRowsInSection || _asyncDataSourceFlags.tableViewNumberOfRowsInSection, @"Data source must implement tableNode:numberOfRowsInSection:");
}
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
}
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
- (void)setAsyncDelegate:(id<ASTableDelegate>)asyncDelegate
{
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
@@ -324,19 +349,43 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
_asyncDelegate = asyncDelegate;
_proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
_asyncDelegateFlags.asyncDelegateScrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)];
if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath == NO) {
_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPathDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)];
}
_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)];
_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
_asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)];
_asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)];
_asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
_asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
_asyncDelegateFlags.asyncDelegateTableViewConstrainedSizeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:constrainedSizeForRowAtIndexPath:)];
_asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
_asyncDelegateFlags.tableViewWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)];
if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow == NO) {
_asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)];
}
_asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)];
_asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
_asyncDelegateFlags.tableViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)];
_asyncDelegateFlags.tableNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableNode:willBeginBatchFetchWithContext:)];
_asyncDelegateFlags.shouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)];
_asyncDelegateFlags.shouldBatchFetchForTableNode = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableNode:)];
_asyncDelegateFlags.scrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
_asyncDelegateFlags.scrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
_asyncDelegateFlags.tableViewConstrainedSizeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:constrainedSizeForRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeConstrainedSizeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:constrainedSizeForRowAtIndexPath:)];
_asyncDelegateFlags.tableViewWillSelectRow = [_asyncDelegate respondsToSelector:@selector(tableView:willSelectRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeWillSelectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willSelectRowAtIndexPath:)];
_asyncDelegateFlags.tableViewDidSelectRow = [_asyncDelegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeDidSelectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didSelectRowAtIndexPath:)];
_asyncDelegateFlags.tableViewWillDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDeselectRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeWillDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willDeselectRowAtIndexPath:)];
_asyncDelegateFlags.tableViewDidDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeDidDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didDeselectRowAtIndexPath:)];
_asyncDelegateFlags.tableViewShouldHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:shouldHighlightRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeShouldHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:shouldHighlightRowAtIndexPath:)];
_asyncDelegateFlags.tableViewDidHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:didHighlightRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeDidHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didHighlightRowAtIndexPath:)];
_asyncDelegateFlags.tableViewDidUnhighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:didUnhighlightRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeDidUnhighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didUnhighlightRowAtIndexPath:)];
_asyncDelegateFlags.tableViewShouldShowMenuForRow = [_asyncDelegate respondsToSelector:@selector(tableView:shouldShowMenuForRowAtIndexPath:)];
_asyncDelegateFlags.tableNodeShouldShowMenuForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:shouldShowMenuForRowAtIndexPath:)];
_asyncDelegateFlags.tableViewCanPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableView:canPerformAction:forRowAtIndexPath:withSender:)];
_asyncDelegateFlags.tableNodeCanPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:canPerformAction:forRowAtIndexPath:withSender:)];
_asyncDelegateFlags.tableViewPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableView:performAction:forRowAtIndexPath:withSender:)];
_asyncDelegateFlags.tableNodePerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:performAction:forRowAtIndexPath:withSender:)];
}
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
@@ -396,6 +445,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
}
- (ASTableNode *)tableNode
{
return (ASTableNode *)ASViewToDisplayNode(self);
}
- (NSArray<NSArray <ASCellNode *> *> *)completedNodes
{
return [_dataController completedNodes];
@@ -403,12 +457,34 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [_dataController nodeAtIndexPath:indexPath];
return [_dataController nodeAtCompletedIndexPath:indexPath];
}
- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait
{
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
return [self indexPathForNode:node waitingIfNeeded:wait];
}
- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath
{
ASCellNode *node = [self nodeForRowAtIndexPath:indexPath];
return [_dataController indexPathForNode:node];
}
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
{
return [_dataController indexPathForNode:cellNode];
return [self indexPathForNode:cellNode waitingIfNeeded:NO];
}
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait
{
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode];
if (indexPath == nil && wait) {
[_dataController waitUntilAllUpdatesAreCommitted];
indexPath = [_dataController completedIndexPathForNode:cellNode];
}
return indexPath;
}
- (NSArray<ASCellNode *> *)visibleNodes
@@ -450,28 +526,40 @@ 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();
[self waitUntilAllUpdatesAreCommitted];
[super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
}
- (void)scrollToNearestSelectedRowAtScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
{
ASDisplayNodeAssertMainThread();
[self waitUntilAllUpdatesAreCommitted];
[super scrollToNearestSelectedRowAtScrollPosition:scrollPosition animated:animated];
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
* still expects data source index paths for the time being.
*/
- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition
{
ASDisplayNodeAssertMainThread();
[self waitUntilAllUpdatesAreCommitted];
[super selectRowAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition];
indexPath = [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES];
if (indexPath != nil) {
[super selectRowAtIndexPath:indexPath animated:YES scrollPosition:scrollPosition];
} else {
NSLog(@"Warning: Ignoring request to select row at index path %@ because the item did not reach the table view.", indexPath);
}
}
- (void)layoutSubviews
@@ -627,7 +715,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
_ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
cell.delegate = self;
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
ASCellNode *node = [_dataController nodeAtCompletedIndexPath:indexPath];
if (node) {
[_rangeController configureContentView:cell.contentView forCellNode:node];
@@ -652,17 +740,17 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [_dataController numberOfSections];
return [_dataController completedNumberOfSections];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_dataController numberOfRowsInSection:section];
return [_dataController completedNumberOfRowsInSection:section];
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_asyncDataSourceFlags.asyncDataSourceTableViewCanMoveRowAtIndexPath) {
if (_asyncDataSourceFlags.tableViewCanMoveRow) {
return [_asyncDataSource tableView:self canMoveRowAtIndexPath:indexPath];
} else {
return NO;
@@ -671,7 +759,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
if (_asyncDataSourceFlags.asyncDataSourceTableViewMoveRowAtIndexPath) {
if (_asyncDataSourceFlags.tableViewMoveRow) {
[_asyncDataSource tableView:self moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath];
}
// Move node after informing data source in case they call nodeAtIndexPath:
@@ -687,11 +775,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath);
if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath) {
[_asyncDelegate tableView:self willDisplayNode:cellNode forRowAtIndexPath:indexPath];
if (_asyncDelegateFlags.tableNodeWillDisplayNodeForRow) {
[_asyncDelegate tableNode:self.tableNode willDisplayRowWithNode:cellNode];
} else if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
} else if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPathDeprecated) {
[_asyncDelegate tableView:self willDisplayNode:cellNode forRowAtIndexPath:indexPath];
} else if (_asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated) {
[_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath];
}
#pragma clang diagnostic pop
@@ -713,9 +803,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
[_rangeController setNeedsUpdate];
if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath) {
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
if (_asyncDelegateFlags.tableNodeDidEndDisplayingNodeForRow) {
[_asyncDelegate tableNode:self.tableNode didEndDisplayingRowWithNode:cellNode];
} else if (_asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
[_cellsForVisibilityUpdates removeObject:cell];
@@ -723,6 +818,171 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
cellNode.scrollView = nil;
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeWillSelectRow) {
NSIndexPath *result = [self convertIndexPathToTableNode:indexPath];
// If this item was is gone, just let the table view do its default behavior and select.
if (result == nil) {
return indexPath;
} else {
result = [_asyncDelegate tableNode:self.tableNode willSelectRowAtIndexPath:result];
result = [self convertIndexPathFromTableNode:result waitingIfNeeded:YES];
return result;
}
} else if (_asyncDelegateFlags.tableViewWillSelectRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDelegate tableView:self willSelectRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
} else {
return indexPath;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeDidSelectRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
[_asyncDelegate tableNode:self.tableNode didSelectRowAtIndexPath:indexPath];
}
} else if (_asyncDelegateFlags.tableViewDidSelectRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self didSelectRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
}
- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeWillDeselectRow) {
NSIndexPath *result = [self convertIndexPathToTableNode:indexPath];
// If this item was is gone, just let the table view do its default behavior and deselect.
if (result == nil) {
return indexPath;
} else {
result = [_asyncDelegate tableNode:self.tableNode willDeselectRowAtIndexPath:result];
result = [self convertIndexPathFromTableNode:result waitingIfNeeded:YES];
return result;
}
} else if (_asyncDelegateFlags.tableViewWillDeselectRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDelegate tableView:self willDeselectRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
return indexPath;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeDidDeselectRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
[_asyncDelegate tableNode:self.tableNode didDeselectRowAtIndexPath:indexPath];
}
} else if (_asyncDelegateFlags.tableViewDidDeselectRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self didDeselectRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
}
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeShouldHighlightRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
return [_asyncDelegate tableNode:self.tableNode shouldHighlightRowAtIndexPath:indexPath];
}
} else if (_asyncDelegateFlags.tableViewShouldHighlightRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDelegate tableView:self shouldHighlightRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
return YES;
}
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeDidHighlightRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
return [_asyncDelegate tableNode:self.tableNode didHighlightRowAtIndexPath:indexPath];
}
} else if (_asyncDelegateFlags.tableViewDidHighlightRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self didHighlightRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
}
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeDidHighlightRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
return [_asyncDelegate tableNode:self.tableNode didUnhighlightRowAtIndexPath:indexPath];
}
} else if (_asyncDelegateFlags.tableViewDidUnhighlightRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self didUnhighlightRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
}
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if (_asyncDelegateFlags.tableNodeShouldShowMenuForRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
return [_asyncDelegate tableNode:self.tableNode shouldShowMenuForRowAtIndexPath:indexPath];
}
} else if (_asyncDelegateFlags.tableViewShouldShowMenuForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDelegate tableView:self shouldShowMenuForRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
}
return NO;
}
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(nonnull SEL)action forRowAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender
{
if (_asyncDelegateFlags.tableNodeCanPerformActionForRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
return [_asyncDelegate tableNode:self.tableNode canPerformAction:action forRowAtIndexPath:indexPath withSender:sender];
}
} else if (_asyncDelegateFlags.tableViewCanPerformActionForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDelegate tableView:self canPerformAction:action forRowAtIndexPath:indexPath withSender:sender];
#pragma clang diagnostic pop
}
return NO;
}
- (void)tableView:(UITableView *)tableView performAction:(nonnull SEL)action forRowAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender
{
if (_asyncDelegateFlags.tableNodePerformActionForRow) {
indexPath = [self convertIndexPathToTableNode:indexPath];
if (indexPath != nil) {
[_asyncDelegate tableNode:self.tableNode performAction:action forRowAtIndexPath:indexPath withSender:sender];
}
} else if (_asyncDelegateFlags.tableViewPerformActionForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self performAction:action forRowAtIndexPath:indexPath withSender:sender];
#pragma clang diagnostic pop
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
@@ -737,7 +997,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
inScrollView:scrollView
withCellFrame:tableCell.frame];
}
if (_asyncDelegateFlags.asyncDelegateScrollViewDidScroll) {
if (_asyncDelegateFlags.scrollViewDidScroll) {
[_asyncDelegate scrollViewDidScroll:scrollView];
}
}
@@ -755,7 +1015,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset];
}
if (_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset) {
if (_asyncDelegateFlags.scrollViewWillEndDragging) {
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:(targetContentOffset ? : &contentOffset)];
}
}
@@ -767,7 +1027,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
inScrollView:scrollView
withCellFrame:tableViewCell.frame];
}
if (_asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging) {
if (_asyncDelegateFlags.scrollViewWillBeginDragging) {
[_asyncDelegate scrollViewWillBeginDragging:scrollView];
}
}
@@ -779,7 +1039,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
inScrollView:scrollView
withCellFrame:tableViewCell.frame];
}
if (_asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging) {
if (_asyncDelegateFlags.scrollViewDidEndDragging) {
[_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}
}
@@ -841,9 +1101,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (BOOL)canBatchFetch
{
// if the delegate does not respond to this method, there is no point in starting to fetch
BOOL canFetch = _asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext;
if (canFetch && _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView) {
BOOL canFetch = _asyncDelegateFlags.tableNodeWillBeginBatchFetch || _asyncDelegateFlags.tableViewWillBeginBatchFetch;
if (canFetch && _asyncDelegateFlags.shouldBatchFetchForTableNode) {
return [_asyncDelegate shouldBatchFetchForTableNode:self.tableNode];
} else if (canFetch && _asyncDelegateFlags.shouldBatchFetchForTableView) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDelegate shouldBatchFetchForTableView:self];
#pragma clang diagnostic pop
} else {
return canFetch;
}
@@ -882,9 +1147,16 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (void)_beginBatchFetching
{
[_batchContext beginBatchFetching];
if (_asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext) {
if (_asyncDelegateFlags.tableNodeWillBeginBatchFetch) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_asyncDelegate tableNode:self.tableNode willBeginBatchFetchWithContext:_batchContext];
});
} else if (_asyncDelegateFlags.tableViewWillBeginBatchFetch) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[_asyncDelegate tableView:self willBeginBatchFetchWithContext:_batchContext];
#pragma clang diagnostic pop
});
}
}
@@ -1110,24 +1382,46 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
#pragma mark - ASDataControllerDelegate
- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath {
if (![_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]) {
ASCellNodeBlock block = nil;
if (_asyncDataSourceFlags.tableNodeNodeBlockForRow) {
block = [_asyncDataSource tableNode:self.tableNode nodeBlockForRowAtIndexPath:indexPath];
} else if (_asyncDataSourceFlags.tableNodeNodeForRow) {
ASCellNode *node = [_asyncDataSource tableNode:self.tableNode nodeForRowAtIndexPath:indexPath];
if ([node isKindOfClass:[ASCellNode class]]) {
block = ^{
return node;
};
} else {
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableNode:nodeForRowAtIndexPath:. Node: %@", node);
}
} else if (_asyncDataSourceFlags.tableViewNodeBlockForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
block = [_asyncDataSource tableView:self nodeBlockForRowAtIndexPath:indexPath];
} else if (_asyncDataSourceFlags.tableViewNodeForRow) {
ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath];
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
__weak __typeof__(self) weakSelf = self;
return ^{
__typeof__(self) strongSelf = weakSelf;
[node enterHierarchyState:ASHierarchyStateRangeManaged];
if (node.interactionDelegate == nil) {
node.interactionDelegate = strongSelf;
}
return node;
#pragma clang diagnostic pop
if ([node isKindOfClass:[ASCellNode class]]) {
block = ^{
return node;
};
} else {
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableView:nodeForRowAtIndexPath:. Node: %@", node);
}
}
// Handle nil node block
if (block == nil) {
ASDisplayNodeFailAssert(@"ASTableNode could not get a node block for row at index path %@", indexPath);
block = ^{
return [[ASCellNode alloc] init];
};
}
ASCellNodeBlock block = [_asyncDataSource tableView:self nodeBlockForRowAtIndexPath:indexPath];
ASDisplayNodeAssertNotNil(block, @"Invalid block, expected nonnull ASCellNodeBlock");
// Wrap the node block
__weak __typeof__(self) weakSelf = self;
ASCellNodeBlock configuredNodeBlock = ^{
return ^{
__typeof__(self) strongSelf = weakSelf;
ASCellNode *node = (block != nil ? block() : [[ASCellNode alloc] init]);
[node enterHierarchyState:ASHierarchyStateRangeManaged];
@@ -1136,14 +1430,22 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
}
return node;
};
return configuredNodeBlock;
return block;
}
- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
ASSizeRange constrainedSize = kInvalidSizeRange;
if (_asyncDelegateFlags.asyncDelegateTableViewConstrainedSizeForRowAtIndexPath) {
if (_asyncDelegateFlags.tableNodeConstrainedSizeForRow) {
ASSizeRange delegateConstrainedSize = [_asyncDelegate tableNode:self.tableNode constrainedSizeForRowAtIndexPath:indexPath];
// ignore widths in the returned size range (for TableView)
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height),
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
} else if (_asyncDelegateFlags.tableViewConstrainedSizeForRow) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ASSizeRange delegateConstrainedSize = [_asyncDelegate tableView:self constrainedSizeForRowAtIndexPath:indexPath];
#pragma clang diagnostic pop
// ignore widths in the returned size range (for TableView)
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height),
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
@@ -1156,13 +1458,27 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
{
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
if (_asyncDataSourceFlags.tableNodeNumberOfRowsInSection) {
return [_asyncDataSource tableNode:self.tableNode numberOfRowsInSection:section];
} else if (_asyncDataSourceFlags.tableViewNumberOfRowsInSection) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
#pragma clang diagnostic pop
} else {
return 0;
}
}
- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController
{
if (_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView) {
if (_asyncDataSourceFlags.numberOfSectionsInTableNode) {
return [_asyncDataSource numberOfSectionsInTableNode:self.tableNode];
} else if (_asyncDataSourceFlags.numberOfSectionsInTableView) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [_asyncDataSource numberOfSectionsInTableView:self];
#pragma clang diagnostic pop
} else {
return 1; // default section number
}
@@ -1172,10 +1488,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (id<ASEnvironment>)dataControllerEnvironment
{
if (self.tableNode) {
return self.tableNode;
}
return self.strongTableNode;
return self.tableNode;
}
#pragma mark - _ASTableViewCellDelegate
@@ -1215,7 +1528,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (void)nodeSelectedStateDidChange:(ASCellNode *)node
{
NSIndexPath *indexPath = [self indexPathForNode:node];
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:node];
if (indexPath) {
if (node.isSelected) {
[self selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
@@ -1227,7 +1540,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (void)nodeHighlightedStateDidChange:(ASCellNode *)node
{
NSIndexPath *indexPath = [self indexPathForNode:node];
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:node];
if (indexPath) {
[self cellForRowAtIndexPath:indexPath].highlighted = node.isHighlighted;
}
@@ -1271,6 +1584,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
#pragma mark - Helper Methods
/// @note This should be a UIKit index path.
- (BOOL)isIndexPath:(NSIndexPath *)indexPath immediateSuccessorOfIndexPath:(NSIndexPath *)anchor
{
if (!anchor || !indexPath) {
@@ -1280,12 +1594,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
if (anchor.row != [_dataController completedNumberOfRowsInSection:anchor.section] -1) {
return NO; // anchor is not at the end of the section
}
NSInteger nextSection = anchor.section+1;
while([_dataController numberOfRowsInSection:nextSection] == 0) {
while([_dataController completedNumberOfRowsInSection:nextSection] == 0) {
++nextSection;
}