From 8fa092fb77d262235231d931ca8a438e6d55616a Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sat, 4 Jul 2015 20:22:04 -0700 Subject: [PATCH] Complete overhaul of ASFlowLayoutController. Introduced ASIndexPath for efficient handling of index paths in C++ vectors, while maintaining the readability of ".section" and ".row" instead of ".first" and ".second" inside of complicated business logic. Confirmed that the working range calls are firing appropriately during ASTableViewStressTest, including the deallocation of the rich text placeholders provided by ASTextNode. --- AsyncDisplayKit/ASDisplayNode.mm | 1 + AsyncDisplayKit/ASTableView.mm | 9 +- .../ASCollectionViewLayoutController.h | 1 - .../ASCollectionViewLayoutController.mm | 14 -- AsyncDisplayKit/Details/ASDataController.h | 7 +- AsyncDisplayKit/Details/ASDataController.mm | 15 +- .../Details/ASFlowLayoutController.h | 10 +- .../Details/ASFlowLayoutController.mm | 238 +++++++++--------- AsyncDisplayKit/Details/ASIndexPath.h | 84 +++++++ AsyncDisplayKit/Details/ASRangeController.mm | 12 - 10 files changed, 224 insertions(+), 167 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASIndexPath.h diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 212c5c76f1..15f0cfcc34 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1337,6 +1337,7 @@ static NSInteger incrementIfFound(NSInteger i) { { self.layer.contents = nil; _placeholderLayer.contents = nil; + _placeholderImage = nil; } - (void)recursivelyClearContents diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 12fa595119..6473eab376 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -117,7 +117,7 @@ static BOOL _isInterceptedSelector(SEL sel) _ASTableViewProxy *_proxyDelegate; ASDataController *_dataController; - ASCollectionViewLayoutController *_layoutController; + ASFlowLayoutController *_layoutController; ASRangeController *_rangeController; @@ -159,9 +159,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)configureWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled { - UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; - _layoutController = [[ASCollectionViewLayoutController alloc] initWithScrollView:self collectionViewLayout:flowLayout]; - + _layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical]; + _rangeController = [[ASRangeController alloc] init]; _rangeController.layoutController = _layoutController; _rangeController.delegate = self; @@ -169,6 +168,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController.dataSource = self; _dataController.delegate = _rangeController; + + _layoutController.dataSource = _dataController; _asyncDataFetchingEnabled = asyncDataFetchingEnabled; _asyncDataSourceLocked = NO; diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.h b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.h index 3c33ae189a..9aa25db0a9 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.h +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.h @@ -14,6 +14,5 @@ @interface ASCollectionViewLayoutController : ASAbstractLayoutController - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView; -- (instancetype)initWithScrollView:(UIScrollView *)scrollView collectionViewLayout:(UICollectionViewLayout *)layout; @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm index c83a0ac2bd..8655102738 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm @@ -78,20 +78,6 @@ typedef struct ASRangeGeometry ASRangeGeometry; return self; } -- (instancetype)initWithScrollView:(UIScrollView *)scrollView collectionViewLayout:(UICollectionViewLayout *)layout -{ - if (!(self = [super init])) { - return nil; - } - - _scrollableDirections = ASScrollDirectionVerticalDirections; - _scrollView = scrollView; - _collectionViewLayout = layout; - _updateRangeBoundsIndexedByRangeType = std::vector(ASLayoutRangeTypeCount); - return self; -} - - #pragma mark - #pragma mark Index Paths in Range diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 3254655fcb..08dfd4fbf2 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -8,7 +8,7 @@ #import #import - +#import "ASFlowLayoutController.h" @class ASCellNode; @class ASDataController; @@ -97,7 +97,8 @@ typedef NSUInteger ASDataControllerAnimationOptions; * will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called. * For each data updatin, the corresponding methods in delegate will be called. */ -@interface ASDataController : ASDealloc2MainObject +@protocol ASFlowLayoutControllerDataSource; +@interface ASDataController : ASDealloc2MainObject /** Data source for fetching data info. @@ -167,4 +168,6 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths; +- (NSArray *)completedNodes; // This provides efficient access to the entire _completedNodes multidimensional array. + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 25b6518e21..c3d1a916c6 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -54,7 +54,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _pendingEditCommandBlocks = [NSMutableArray array]; _editingTransactionQueue = [[NSOperationQueue alloc] init]; - _editingTransactionQueue.qualityOfService = NSQualityOfServiceUserInitiated; _editingTransactionQueue.maxConcurrentOperationCount = 1; // Serial queue _editingTransactionQueue.name = @"org.AsyncDisplayKit.ASDataController.editingTransactionQueue"; @@ -553,17 +552,15 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths { ASDisplayNodeAssertMainThread(); - - // Make sure that any asynchronous layout operations have finished so that those nodes are present. - // Otherwise a failure case could be: - // - Reload section 2, deleting all current nodes in that section. - // - New nodes are created and sizing is triggered, but they are not yet added to _completedNodes. - // - This method is called and includes an indexPath in section 2. - // - Unless we wait for the layout group to finish, we will crash with array out of bounds looking for the index in _completedNodes. - return ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, [indexPaths sortedArrayUsingSelector:@selector(compare:)]); } +- (NSArray *)completedNodes +{ + ASDisplayNodeAssertMainThread(); + return _completedNodes; +} + #pragma mark - Dealloc - (void)dealloc diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.h b/AsyncDisplayKit/Details/ASFlowLayoutController.h index 0c682594cb..01da06d124 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.h +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.h @@ -15,12 +15,20 @@ typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) { ASFlowLayoutDirectionHorizontal, }; +@protocol ASFlowLayoutControllerDataSource + +- (NSArray *)completedNodes; // This provides access to ASDataController's _completedNodes multidimensional array. + +@end + /** - * The controller for flow layout. + * An optimized flow layout controller that supports only vertical or horizontal scrolling, not simultaneously two-dimensional scrolling. + * It is used for all ASTableViews, and may be used with ASCollectionView. */ @interface ASFlowLayoutController : ASAbstractLayoutController @property (nonatomic, readonly, assign) ASFlowLayoutDirection layoutDirection; +@property (nonatomic) id dataSource; - (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection; diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.mm b/AsyncDisplayKit/Details/ASFlowLayoutController.mm index 5bac784679..52b9e6e880 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.mm +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.mm @@ -7,110 +7,66 @@ */ #import "ASFlowLayoutController.h" +#import "ASAssert.h" +#import "ASDisplayNode.h" +#import "ASIndexPath.h" #include #include #include -#import "ASAssert.h" - static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; -@interface ASFlowLayoutController() { - std::vector > _nodeSizes; - - std::pair _visibleRangeStartPos; - std::pair _visibleRangeEndPos; - - std::vector> _rangeStartPos; - std::vector> _rangeEndPos; +@interface ASFlowLayoutController() +{ + ASIndexPathRange _visibleRange; + std::vector _rangesByType; // All ASLayoutRangeTypes besides visible. } @end @implementation ASFlowLayoutController -- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection { +- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection +{ if (!(self = [super init])) { return nil; } - _layoutDirection = layoutDirection; - + _rangesByType = std::vector(ASLayoutRangeTypeCount); return self; } -#pragma mark - Editing - -- (void)insertNodesAtIndexPaths:(NSArray *)indexPaths withSizes:(NSArray *)nodeSizes -{ - ASDisplayNodeAssert(indexPaths.count == nodeSizes.count, @"Inconsistent index paths and node size"); - - [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { - std::vector &v = _nodeSizes[indexPath.section]; - v.insert(v.begin() + indexPath.row, [(NSValue *)nodeSizes[idx] CGSizeValue]); - }]; -} - -- (void)deleteNodesAtIndexPaths:(NSArray *)indexPaths -{ - [indexPaths enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { - std::vector &v = _nodeSizes[indexPath.section]; - v.erase(v.begin() + indexPath.row); - }]; -} - -- (void)insertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet -{ - __block int cnt = 0; - [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - NSArray *nodes = sections[cnt++]; - std::vector v; - v.reserve(nodes.count); - - for (int i = 0; i < nodes.count; i++) { - v.push_back([nodes[i] CGSizeValue]); - } - - _nodeSizes.insert(_nodeSizes.begin() + idx, v); - }]; -} - -- (void)deleteSectionsAtIndexSet:(NSIndexSet *)indexSet { - [indexSet enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *stop) { - _nodeSizes.erase(_nodeSizes.begin() +idx); - }]; -} - #pragma mark - Visible Indices - (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType { - if (!indexPaths.count) { + if (!indexPaths.count || rangeType >= _rangesByType.size()) { return NO; } - std::pair rangeStartPos, rangeEndPos; - - if (rangeType < _rangeStartPos.size() && rangeType < _rangeEndPos.size()) { - rangeStartPos = _rangeStartPos[rangeType]; - rangeEndPos = _rangeEndPos[rangeType]; - } - - std::pair startPos, endPos; - ASFindIndexPathRange(indexPaths, startPos, endPos); - - if (rangeStartPos >= startPos || rangeEndPos <= endPos) { + ASIndexPathRange existingRange = _rangesByType[rangeType]; + ASIndexPathRange newRange = [self indexPathRangeForIndexPaths:indexPaths]; + + ASIndexPath maximumStart = ASIndexPathMaximum(existingRange.start, newRange.start); + ASIndexPath minimumEnd = ASIndexPathMinimum(existingRange.end, newRange.end); + + if (ASIndexPathEqualToIndexPath(maximumStart, existingRange.start) || ASIndexPathEqualToIndexPath(minimumEnd, existingRange.end)) { return YES; } - return ASFlowLayoutDistance(startPos, _visibleRangeStartPos, _nodeSizes) > ASFlowLayoutDistance(_visibleRangeStartPos, rangeStartPos, _nodeSizes) * kASFlowLayoutControllerRefreshingThreshold || - ASFlowLayoutDistance(endPos, _visibleRangeEndPos, _nodeSizes) > ASFlowLayoutDistance(_visibleRangeEndPos, rangeEndPos, _nodeSizes) * kASFlowLayoutControllerRefreshingThreshold; + NSInteger newStartDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.start, newRange.start)]; + NSInteger existingStartDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.start, existingRange.start)] * kASFlowLayoutControllerRefreshingThreshold; + + NSInteger newEndDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.end, newRange.end)]; + NSInteger existingEndDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.end, existingRange.end)] * kASFlowLayoutControllerRefreshingThreshold; + + return (newStartDelta > existingStartDelta) || (newEndDelta > existingEndDelta); } - (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths { - ASFindIndexPathRange(indexPaths, _visibleRangeStartPos, _visibleRangeEndPos); + _visibleRange = [self indexPathRangeForIndexPaths:indexPaths]; } /** @@ -138,100 +94,134 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; CGFloat backScreens = scrollDirection == leadingDirection ? tuningParameters.leadingBufferScreenfuls : tuningParameters.trailingBufferScreenfuls; CGFloat frontScreens = scrollDirection == leadingDirection ? tuningParameters.trailingBufferScreenfuls : tuningParameters.leadingBufferScreenfuls; - std::pair startIter = ASFindIndexForRange(_nodeSizes, _visibleRangeStartPos, - backScreens * viewportScreenMetric, _layoutDirection); - std::pair endIter = ASFindIndexForRange(_nodeSizes, _visibleRangeEndPos, frontScreens * viewportScreenMetric, _layoutDirection); + + ASIndexPath startPath = [self findIndexPathAtDistance:(-backScreens * viewportScreenMetric) fromIndexPath:_visibleRange.start]; + ASIndexPath endPath = [self findIndexPathAtDistance:(frontScreens * viewportScreenMetric) fromIndexPath:_visibleRange.end]; + ASDisplayNodeAssert(startPath.section <= endPath.section, @"startPath should never begin at a further position than endPath"); + NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; - while (startIter != endIter) { - [indexPathSet addObject:[NSIndexPath indexPathForRow:startIter.second inSection:startIter.first]]; - startIter.second++; + NSArray *completedNodes = [_dataSource completedNodes]; + + while (!ASIndexPathEqualToIndexPath(startPath, endPath)) { + [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:startPath]]; + startPath.row++; // Once we reach the end of the section, advance to the next one. Keep advancing if the next section is zero-sized. - while (startIter.second == _nodeSizes[startIter.first].size() && startIter.first < _nodeSizes.size()) { - startIter.second = 0; - startIter.first++; + while (startPath.row >= [(NSArray *)completedNodes[startPath.section] count] && startPath.section < completedNodes.count - 1) { + startPath.row = 0; + startPath.section++; + ASDisplayNodeAssert(startPath.section <= endPath.section, @"startPath should never reach a further section than endPath"); } } - [indexPathSet addObject:[NSIndexPath indexPathForRow:endIter.second inSection:endIter.first]]; + [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:endPath]]; return indexPathSet; } #pragma mark - Utility -static void ASFindIndexPathRange(NSArray *indexPaths, std::pair &startPos, std::pair &endPos) - +- (ASIndexPathRange)indexPathRangeForIndexPaths:(NSArray *)indexPaths { - NSIndexPath *initialIndexPath = [indexPaths firstObject]; - startPos = endPos = {initialIndexPath.section, initialIndexPath.row}; + // Set up an initial value so the MIN and MAX can work in the enumeration. + __block ASIndexPath currentIndexPath = [[indexPaths firstObject] ASIndexPathValue]; + __block ASIndexPathRange range; + range.start = currentIndexPath; + range.end = currentIndexPath; + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { - std::pair p(indexPath.section, indexPath.row); - startPos = MIN(startPos, p); - endPos = MAX(endPos, p); + currentIndexPath = [indexPath ASIndexPathValue]; + range.start = ASIndexPathMinimum(range.start, currentIndexPath); + range.end = ASIndexPathMaximum(range.end, currentIndexPath); }]; + return range; } -static const std::pair ASFindIndexForRange(const std::vector> &nodes, - const std::pair &pos, - CGFloat range, - ASFlowLayoutDirection layoutDirection) +- (ASIndexPath)findIndexPathAtDistance:(CGFloat)distance fromIndexPath:(ASIndexPath)start { - std::pair cur = pos, pre = pos; + // "end" is the index path we'll advance until we have gone far enough from "start" to reach "distance" + ASIndexPath end = start; + // "previous" will store one iteration before "end", in case we go too far and need to reset "end" to be "previous" + ASIndexPath previous = start; - if (range < 0.0 && cur.first >= 0 && cur.first < nodes.size() && cur.second >= 0 && cur.second < nodes[cur.first].size()) { - // search backward - while (range < 0.0 && cur.first >= 0 && cur.second >= 0) { - pre = cur; - CGSize size = nodes[cur.first][cur.second]; - range += layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height; - cur.second--; - while (cur.second < 0 && cur.first > 0) { - cur.second = (int)nodes[--cur.first].size() - 1; + NSArray *completedNodes = [_dataSource completedNodes]; + NSUInteger numberOfSections = [completedNodes count]; + NSUInteger numberOfRowsInSection = [(NSArray *)completedNodes[end.section] count]; + + // If "distance" is negative, advance "end" backwards across rows and sections. + // Otherwise, advance forward. In either case, bring "distance" closer to zero by the dimension of each row passed. + if (distance < 0.0 && end.section >= 0 && end.section < numberOfSections && end.row >= 0 && end.row < numberOfRowsInSection) { + while (distance < 0.0 && end.section >= 0 && end.row >= 0) { + previous = end; + ASDisplayNode *node = completedNodes[end.section][end.row]; + CGSize size = node.calculatedSize; + distance += (_layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height); + end.row--; + // If we've gone to a negative row, set to the last row of the previous section. While loop is required to handle empty sections. + while (end.row < 0 && end.section > 0) { + end.section--; + numberOfRowsInSection = [(NSArray *)completedNodes[end.section] count]; + end.row = numberOfRowsInSection - 1; } } - if (cur.second < 0) { - cur = pre; + if (end.row < 0) { + end = previous; } } else { - // search forward - while (range > 0.0 && cur.first >= 0 && cur.first < nodes.size() && cur.second >= 0 && cur.second < nodes[cur.first].size()) { - pre = cur; - CGSize size = nodes[cur.first][cur.second]; - range -= layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height; + while (distance > 0.0 && end.section >= 0 && end.section < numberOfSections && end.row >= 0 && end.row < numberOfRowsInSection) { + previous = end; + ASDisplayNode *node = completedNodes[end.section][end.row]; + CGSize size = node.calculatedSize; + distance -= _layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height; - cur.second++; - while (cur.second == nodes[cur.first].size() && cur.first < (int)nodes.size() - 1) { - cur.second = 0; - cur.first++; + end.row++; + // If we've gone beyond the section, reset to the beginning of the next section. While loop is required to handle empty sections. + while (end.row >= numberOfRowsInSection && end.section < numberOfSections - 1) { + end.row = 0; + end.section++; + numberOfRowsInSection = [(NSArray *)completedNodes[end.section] count]; } } - if (cur.second == nodes[cur.first].size()) { - cur = pre; + if (end.row >= numberOfRowsInSection) { + end = previous; } } - return cur; + return end; } -static int ASFlowLayoutDistance(const std::pair &start, const std::pair &end, const std::vector> &nodes) +- (NSInteger)flowLayoutDistanceForRange:(ASIndexPathRange)range { - if (start == end) { + // This method should only be called with the range in proper order (start comes before end). + ASDisplayNodeAssert(ASIndexPathEqualToIndexPath(ASIndexPathMinimum(range.start, range.end), range.start), @"flowLayoutDistanceForRange: called with invalid range"); + + if (ASIndexPathEqualToIndexPath(range.start, range.end)) { return 0; - } else if (start > end) { - return - ASFlowLayoutDistance(end, start, nodes); } + + NSInteger totalRowCount = 0; + NSUInteger numberOfRowsInSection = 0; + NSArray *completedNodes = [_dataSource completedNodes]; - int res = 0; - - for (int i = start.first; i <= end.first; i++) { - res += (i == end.first ? end.second + 1 : nodes[i].size()) - (i == start.first ? start.second : 0); + for (NSInteger section = range.start.section; section <= range.end.section; section++) { + numberOfRowsInSection = [(NSArray *)completedNodes[section] count]; + totalRowCount += numberOfRowsInSection; + + if (section == range.start.section) { + // For the start section, make sure we don't count the rows before the start row. + totalRowCount -= range.start.row; + } else if (section == range.end.section) { + // For the start section, make sure we don't count the rows after the end row. + totalRowCount -= (numberOfRowsInSection - (range.end.row + 1)); + } } - - return res; + + ASDisplayNodeAssert(totalRowCount >= 0, @"totalRowCount in flowLayoutDistanceForRange: should not be negative"); + return totalRowCount; } @end diff --git a/AsyncDisplayKit/Details/ASIndexPath.h b/AsyncDisplayKit/Details/ASIndexPath.h new file mode 100644 index 0000000000..9eed7dd247 --- /dev/null +++ b/AsyncDisplayKit/Details/ASIndexPath.h @@ -0,0 +1,84 @@ +// +// ASIndexPath.h +// Pods +// +// Created by Scott Goodson on 7/4/15. +// +// A much more efficient way to handle index paths than NSIndexPath. +// For best results, use C++ vectors; NSValue wrapping with Cocoa collections +// would make NSIndexPath a much better choice. +// + +typedef struct { + NSInteger section; + NSInteger row; +} ASIndexPath; + +typedef struct { + ASIndexPath start; + ASIndexPath end; +} ASIndexPathRange; + +ASIndexPath ASIndexPathMake(NSInteger section, NSInteger row) +{ + ASIndexPath indexPath; + indexPath.section = section; + indexPath.row = row; + return indexPath; +} + +BOOL ASIndexPathEqualToIndexPath(ASIndexPath first, ASIndexPath second) +{ + return (first.section == second.section && first.row == second.row); +} + +ASIndexPath ASIndexPathMinimum(ASIndexPath first, ASIndexPath second) +{ + if (first.section < second.section) { + return first; + } else if (first.section > second.section) { + return second; + } else { + return (first.row < second.row ? first : second); + } +} + +ASIndexPath ASIndexPathMaximum(ASIndexPath first, ASIndexPath second) +{ + if (first.section > second.section) { + return first; + } else if (first.section < second.section) { + return second; + } else { + return (first.row > second.row ? first : second); + } +} + +ASIndexPathRange ASIndexPathRangeMake(ASIndexPath first, ASIndexPath second) +{ + ASIndexPathRange range; + range.start = ASIndexPathMinimum(first, second); + range.end = ASIndexPathMaximum(first, second); + return range; +} + +BOOL ASIndexPathRangeEqualToIndexPathRange(ASIndexPathRange first, ASIndexPathRange second) +{ + return ASIndexPathEqualToIndexPath(first.start, second.start) && ASIndexPathEqualToIndexPath(first.end, second.end); +} + +@interface NSIndexPath (ASIndexPathAdditions) ++ (NSIndexPath *)indexPathWithASIndexPath:(ASIndexPath)indexPath; +- (ASIndexPath)ASIndexPathValue; +@end + +@implementation NSIndexPath (ASIndexPathAdditions) ++ (NSIndexPath *)indexPathWithASIndexPath:(ASIndexPath)indexPath +{ + return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];; +} +- (ASIndexPath)ASIndexPathValue +{ + return ASIndexPathMake(self.section, self.row); +} +@end diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 1a6afed732..f41d9d291c 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -191,9 +191,6 @@ }]; ASDisplayNodePerformBlockOnMainThread(^{ - if ([_layoutController respondsToSelector:@selector(insertNodesAtIndexPaths:withSizes:)]) { - [_layoutController insertNodesAtIndexPaths:indexPaths withSizes:nodeSizes]; - } _rangeIsValid = NO; [_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; }); @@ -201,9 +198,6 @@ - (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodePerformBlockOnMainThread(^{ - if ([_layoutController respondsToSelector:@selector(deleteNodesAtIndexPaths:)]) { - [_layoutController deleteNodesAtIndexPaths:indexPaths]; - } _rangeIsValid = NO; [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; }); @@ -223,9 +217,6 @@ }]; ASDisplayNodePerformBlockOnMainThread(^{ - if ([_layoutController respondsToSelector:@selector(insertSections:atIndexSet:)]) { - [_layoutController insertSections:sectionNodeSizes atIndexSet:indexSet]; - } _rangeIsValid = NO; [_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; }); @@ -233,9 +224,6 @@ - (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodePerformBlockOnMainThread(^{ - if ([_layoutController respondsToSelector:@selector(deleteSectionsAtIndexSet:)]) { - [_layoutController deleteSectionsAtIndexSet:indexSet]; - } _rangeIsValid = NO; [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; });