diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index deb14f55f2..402af31fe4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -95,8 +95,6 @@ 34EFC7731B701D0700AD841F /* ASAbsoluteLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED181B17843500DA7C62 /* ASAbsoluteLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED191B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm */; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */; }; - 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; }; 509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; }; 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */; }; @@ -262,8 +260,8 @@ B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.mm */; }; B35062171B010EFD0018CF92 /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521A1A3F83C40061C0BA /* ASDataController.mm */; }; - B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */; }; + B350621B1B010EFD0018CF92 /* ASTableLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */; settings = {ATTRIBUTES = (Private, ); }; }; + B350621C1B010EFD0018CF92 /* ASTableLayoutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4640521C1A3F83C40061C0BA /* ASTableLayoutController.m */; }; B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E7195D050800B7D73C /* ASHighlightOverlayLayer.mm */; }; B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -533,12 +531,10 @@ 299DA1A81A828D2900162D41 /* ASBatchContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBatchContext.mm; sourceTree = ""; }; 29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASBasicImageDownloaderContextTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTableViewTests.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIndexPath.h; sourceTree = ""; }; - 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIndexPath.m; sourceTree = ""; }; 464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDataController.h; sourceTree = ""; }; 4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDataController.mm; sourceTree = ""; }; - 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASFlowLayoutController.h; sourceTree = ""; }; - 4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASFlowLayoutController.mm; sourceTree = ""; }; + 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = ""; }; + 4640521C1A3F83C40061C0BA /* ASTableLayoutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableLayoutController.m; sourceTree = ""; }; 4640521D1A3F83C40061C0BA /* ASLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutController.h; sourceTree = ""; }; 683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Deprecated.h"; sourceTree = ""; }; 68355B2E1CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "ASImageNode+AnimatedImage.mm"; sourceTree = ""; }; @@ -1062,15 +1058,13 @@ 68C215571DE10D330019C4BC /* ASCollectionViewLayoutInspector.m */, 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */, 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */, - 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */, - 4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */, + 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */, + 4640521C1A3F83C40061C0BA /* ASTableLayoutController.m */, 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */, 058D09E7195D050800B7D73C /* ASHighlightOverlayLayer.mm */, 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */, 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */, 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */, - 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */, - 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */, 4640521D1A3F83C40061C0BA /* ASLayoutController.h */, 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */, 68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */, @@ -1437,13 +1431,12 @@ CC0F88601E4280B800576FED /* _ASCollectionViewCell.h in Headers */, B35062001B010EFD0018CF92 /* ASEditableTextNode.h in Headers */, 680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */, - B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */, + B350621B1B010EFD0018CF92 /* ASTableLayoutController.h in Headers */, B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */, C78F7E2B1BF7809800CDEAFC /* ASTableNode.h in Headers */, 7AB338671C55B3460055FDE8 /* ASRelativeLayoutSpec.h in Headers */, B35062021B010EFD0018CF92 /* ASImageNode.h in Headers */, B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */, - 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, 34EFC75F1B701C8600AD841F /* ASInsetLayoutSpec.h in Headers */, 34EFC7671B701CD900AD841F /* ASLayout.h in Headers */, DBDB83951C6E879900D0098C /* ASPagerFlowLayout.h in Headers */, @@ -1873,7 +1866,7 @@ B35062011B010EFD0018CF92 /* ASEditableTextNode.mm in Sources */, 254C6B881BF94F8A003EC431 /* ASTextKitRenderer.mm in Sources */, CC3B208C1C3F7A5400798563 /* ASWeakSet.m in Sources */, - B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */, + B350621C1B010EFD0018CF92 /* ASTableLayoutController.m in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, 9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */, DE89C17B1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m in Sources */, @@ -1883,7 +1876,6 @@ 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */, B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */, 254C6B821BF94F8A003EC431 /* ASTextKitComponents.mm in Sources */, - 430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */, 34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */, AC6145441D8AFD4F003D62A2 /* ASSection.m in Sources */, 34EFC75E1B701BF000AD841F /* ASInternalHelpers.m in Sources */, diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index d09f2939a0..38ff2e91ed 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -25,6 +25,7 @@ #import #import #import +#import #import #import @@ -120,7 +121,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; ASTableViewProxy *_proxyDataSource; ASTableViewProxy *_proxyDelegate; - ASFlowLayoutController *_layoutController; + ASTableLayoutController *_layoutController; ASRangeController *_rangeController; @@ -250,7 +251,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)configureWithDataControllerClass:(Class)dataControllerClass eventLog:(ASEventLog *)eventLog { - _layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical]; + _layoutController = [[ASTableLayoutController alloc] initWithTableView:self]; _rangeController = [[ASRangeController alloc] init]; _rangeController.layoutController = _layoutController; @@ -260,8 +261,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _dataController = [[dataControllerClass alloc] initWithDataSource:self eventLog:eventLog]; _dataController.delegate = _rangeController; _dataController.environmentDelegate = self; - - _layoutController.dataSource = _dataController; _leadingScreensForBatching = 2.0; _batchContext = [[ASBatchContext alloc] init]; diff --git a/AsyncDisplayKit/AsyncDisplayKit+Debug.m b/AsyncDisplayKit/AsyncDisplayKit+Debug.m index bc5847624c..5c8339de5a 100644 --- a/AsyncDisplayKit/AsyncDisplayKit+Debug.m +++ b/AsyncDisplayKit/AsyncDisplayKit+Debug.m @@ -11,6 +11,7 @@ // #import +#import #import #import #import diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/AsyncDisplayKit/AsyncDisplayKit.h index 1fb307f804..888a09126a 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/AsyncDisplayKit/AsyncDisplayKit.h @@ -88,7 +88,6 @@ #import #import #import -#import #import #import #import diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index bcd7776f07..9bea39cc97 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -13,7 +13,6 @@ #import #import #import -#import #import #ifdef __cplusplus #import @@ -112,7 +111,7 @@ extern NSString * const ASCollectionInvalidUpdateException; * will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called. * For each data updating, the corresponding methods in delegate will be called. */ -@interface ASDataController : NSObject +@interface ASDataController : NSObject - (instancetype)initWithDataSource:(id)dataSource eventLog:(nullable ASEventLog *)eventLog NS_DESIGNATED_INITIALIZER; diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.h b/AsyncDisplayKit/Details/ASFlowLayoutController.h deleted file mode 100644 index 0249d43c3f..0000000000 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// ASFlowLayoutController.h -// AsyncDisplayKit -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class ASCellNode; - -typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) { - ASFlowLayoutDirectionVertical, - ASFlowLayoutDirectionHorizontal, -}; - -@protocol ASFlowLayoutControllerDataSource - -- (NSArray *> *)completedNodes; // This provides access to ASDataController's _completedNodes multidimensional array. - -@end - -/** - * 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, readwrite, weak) id dataSource; - -- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.mm b/AsyncDisplayKit/Details/ASFlowLayoutController.mm deleted file mode 100644 index 92390bc078..0000000000 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.mm +++ /dev/null @@ -1,201 +0,0 @@ -// -// ASFlowLayoutController.mm -// AsyncDisplayKit -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// - -#import -#import -#import -#import -#import - -#include -#include - -@interface ASFlowLayoutController() -{ - ASIndexPathRange _visibleRange; - std::vector _rangesByType; // All ASLayoutRangeTypes besides visible. -} - -@end - -@implementation ASFlowLayoutController - -- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection -{ - if (!(self = [super init])) { - return nil; - } - _layoutDirection = layoutDirection; - _rangesByType = std::vector(ASLayoutRangeTypeCount); - return self; -} - -#pragma mark - Visible Indices - -- (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths -{ - _visibleRange = [self indexPathRangeForIndexPaths:indexPaths]; -} - -/** - * IndexPath array for the element in the working range. - */ - -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType -{ - CGSize viewportSize = [self viewportSize]; - - CGFloat viewportDirectionalSize = 0.0; - ASDirectionalScreenfulBuffer directionalBuffer = { 0, 0 }; - ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; - - if (_layoutDirection == ASFlowLayoutDirectionHorizontal) { - viewportDirectionalSize = viewportSize.width; - directionalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection, tuningParameters); - } else { - viewportDirectionalSize = viewportSize.height; - directionalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters); - } - - ASIndexPath startPath = [self findIndexPathAtDistance:(-directionalBuffer.negativeDirection * viewportDirectionalSize) - fromIndexPath:_visibleRange.start]; - - ASIndexPath endPath = [self findIndexPathAtDistance:(directionalBuffer.positiveDirection * viewportDirectionalSize) - fromIndexPath:_visibleRange.end]; - - ASDisplayNodeAssert(startPath.section <= endPath.section, @"startPath should never begin at a further position than endPath"); - - NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; - - NSArray *completedNodes = [_dataSource completedNodes]; - - ASIndexPath currPath = startPath; - - while (!ASIndexPathEqualToIndexPath(currPath, endPath)) { - [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:currPath]]; - currPath.row++; - - // Once we reach the end of the section, advance to the next one. Keep advancing if the next section is zero-sized. - while (currPath.row >= [(NSArray *)completedNodes[currPath.section] count] && currPath.section < endPath.section) { - currPath.row = 0; - currPath.section++; - } - } - ASDisplayNodeAssert(currPath.section <= endPath.section, @"currPath should never reach a further section than endPath"); - - [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:endPath]]; - - return indexPathSet; -} - -#pragma mark - Utility - -- (ASIndexPathRange)indexPathRangeForIndexPaths:(NSArray *)indexPaths -{ - // 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; - - for (NSIndexPath *indexPath in indexPaths) { - currentIndexPath = [indexPath ASIndexPathValue]; - range.start = ASIndexPathMinimum(range.start, currentIndexPath); - range.end = ASIndexPathMaximum(range.end, currentIndexPath); - } - return range; -} - -- (ASIndexPath)findIndexPathAtDistance:(CGFloat)distance fromIndexPath:(ASIndexPath)start -{ - // "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; - - 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 (end.row < 0) { - end = previous; - } - } else { - 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; - - 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 (end.row >= numberOfRowsInSection) { - end = previous; - } - } - - return end; -} - -- (NSInteger)flowLayoutDistanceForRange:(ASIndexPathRange)range -{ - // 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; - } - - NSInteger totalRowCount = 0; - NSUInteger numberOfRowsInSection = 0; - NSArray *completedNodes = [_dataSource completedNodes]; - - 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)); - } - } - - 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 deleted file mode 100644 index 32ae9f9add..0000000000 --- a/AsyncDisplayKit/Details/ASIndexPath.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// ASIndexPath.h -// AsyncDisplayKit -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// - -#import -#import - -typedef struct { - NSInteger section; - NSInteger row; -} ASIndexPath; - -typedef struct { - ASIndexPath start; - ASIndexPath end; -} ASIndexPathRange; - -NS_ASSUME_NONNULL_BEGIN - -ASDISPLAYNODE_EXTERN_C_BEGIN - - -extern ASIndexPath ASIndexPathMake(NSInteger section, NSInteger row); - -extern BOOL ASIndexPathEqualToIndexPath(ASIndexPath first, ASIndexPath second); - -extern ASIndexPath ASIndexPathMinimum(ASIndexPath first, ASIndexPath second); - -extern ASIndexPath ASIndexPathMaximum(ASIndexPath first, ASIndexPath second); - -extern ASIndexPathRange ASIndexPathRangeMake(ASIndexPath first, ASIndexPath second); - -extern BOOL ASIndexPathRangeEqualToIndexPathRange(ASIndexPathRange first, ASIndexPathRange second); - -ASDISPLAYNODE_EXTERN_C_END - -@interface NSIndexPath (ASIndexPathAdditions) - -+ (NSIndexPath *)indexPathWithASIndexPath:(ASIndexPath)indexPath; -- (ASIndexPath)ASIndexPathValue; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASIndexPath.m b/AsyncDisplayKit/Details/ASIndexPath.m deleted file mode 100644 index 603d6445f8..0000000000 --- a/AsyncDisplayKit/Details/ASIndexPath.m +++ /dev/null @@ -1,75 +0,0 @@ -// -// ASIndexPath.m -// AsyncDisplayKit -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// - -#import - -#import - -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); -} - -@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/ASLayoutController.h b/AsyncDisplayKit/Details/ASLayoutController.h index 1332033d44..fd4a6d367b 100644 --- a/AsyncDisplayKit/Details/ASLayoutController.h +++ b/AsyncDisplayKit/Details/ASLayoutController.h @@ -39,7 +39,7 @@ ASDISPLAYNODE_EXTERN_C_END - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; @optional diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index c0e85bec95..14fb999d16 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -11,6 +11,7 @@ #import #import #import +#import #import #import #import diff --git a/AsyncDisplayKit/Details/ASTableLayoutController.h b/AsyncDisplayKit/Details/ASTableLayoutController.h new file mode 100644 index 0000000000..61a50fba1d --- /dev/null +++ b/AsyncDisplayKit/Details/ASTableLayoutController.h @@ -0,0 +1,30 @@ +// +// ASTableLayoutController.h +// AsyncDisplayKit +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UITableView; + +/** + * A layout controller designed for use with UITableView. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTableLayoutController : ASAbstractLayoutController + +@property (nonatomic, weak, readonly) UITableView *tableView; + +- (instancetype)initWithTableView:(UITableView *)tableView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASTableLayoutController.m b/AsyncDisplayKit/Details/ASTableLayoutController.m new file mode 100644 index 0000000000..294f69372d --- /dev/null +++ b/AsyncDisplayKit/Details/ASTableLayoutController.m @@ -0,0 +1,200 @@ +// +// ASTableLayoutController.m +// AsyncDisplayKit +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +#import + +#import + +@interface ASTableLayoutController() +@property (nonatomic, strong) NSIndexPath *minVisibleIndexPath; +@property (nonatomic, strong) NSIndexPath *maxVisibleIndexPath; +@end + +@implementation ASTableLayoutController + +- (instancetype)initWithTableView:(UITableView *)tableView +{ + if (!(self = [super init])) { + return nil; + } + _tableView = tableView; + return self; +} + +#pragma mark - Visible Indices + +- (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths +{ + _minVisibleIndexPath = [ASTableLayoutController fastArrayMin:indexPaths]; + _maxVisibleIndexPath = [ASTableLayoutController fastArrayMax:indexPaths]; +} + +/** + * IndexPath array for the element in the working range. + */ + +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + if (_minVisibleIndexPath == nil) { + return [NSSet set]; + } + + CGSize viewportSize = [self viewportSize]; + + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + + ASDirectionalScreenfulBuffer directionalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters); + + NSIndexPath *startPath = [self findIndexPathAtDistance:(-directionalBuffer.negativeDirection * viewportSize.height) + fromIndexPath:_minVisibleIndexPath]; + + NSIndexPath *endPath = [self findIndexPathAtDistance:(directionalBuffer.positiveDirection * viewportSize.height) + fromIndexPath:_maxVisibleIndexPath]; + + NSSet *indexPaths = [self indexPathsFromIndexPath:startPath toIndexPath:endPath]; + return indexPaths; +} + +#pragma mark - Utility + +- (NSIndexPath *)findIndexPathAtDistance:(CGFloat)distance fromIndexPath:(NSIndexPath *)start +{ + BOOL downward = (distance >= 0); + CGRect startRowRect = [_tableView rectForRowAtIndexPath:start]; + CGFloat targetY = distance + (downward ? CGRectGetMaxY(startRowRect) : CGRectGetMinY(startRowRect)); + + // Before first row. + NSIndexPath *firstIndexPath = [self firstIndexPathInTableView]; + if (targetY <= CGRectGetMaxY([_tableView rectForRowAtIndexPath:firstIndexPath])) { + return firstIndexPath; + } + + // After last row. + NSIndexPath *lastIndexPath = [self lastIndexPathInTableView]; + if (targetY >= CGRectGetMinY([_tableView rectForRowAtIndexPath:lastIndexPath])) { + return lastIndexPath; + } + + /** + * There may not be a row at any given EXACT point, for these possible reasons: + * - There is a section header/footer at that point. + * - That point is beyond the start/end of the table content. (Handled above) + * + * Solution: Make a search rect, and if we don't + * find any rows, keep doubling its height and searching again. In practice, + * this will virtually always find a row on the first try (unless you have a + * tall section header and we land near the middle.) + */ + NSIndexPath *result = nil; + for (CGRect searchRect = [ASTableLayoutController initialSearchRectDownward:downward targetY:targetY]; + // continue while result is nil + result == nil; + // grow search rect after each loop + searchRect = [ASTableLayoutController growSearchRect:searchRect downward:downward]) { + NSArray *rows = [_tableView indexPathsForRowsInRect:searchRect]; + if (downward) { + result = [ASTableLayoutController fastArrayMin:rows]; + } else { + result = [ASTableLayoutController fastArrayMax:rows]; + } + } + return result; +} + +- (NSSet *)indexPathsFromIndexPath:(NSIndexPath *)startIndexPath toIndexPath:(NSIndexPath *)endIndexPath +{ + ASDisplayNodeAssert([startIndexPath compare:endIndexPath] != NSOrderedDescending, @"Index paths must be in nondescending order. Start: %@, end %@", startIndexPath, endIndexPath); + + NSMutableSet *result = [NSMutableSet set]; + NSInteger const endSection = endIndexPath.section; + NSInteger i = startIndexPath.row; + for (NSInteger s = startIndexPath.section; s <= endSection; s++) { + // If end section, row <= end.item. Otherwise (row <= sectionRowCount - 1). + NSInteger const rowLimit = (s == endSection ? endIndexPath.row : ([_tableView numberOfRowsInSection:s] - 1)); + for (; i <= rowLimit; i++) { + [result addObject:[NSIndexPath indexPathForRow:i inSection:s]]; + } + i = 0; + } + return result; +} + +- (nullable NSIndexPath *)firstIndexPathInTableView +{ + NSInteger sectionCount = _tableView.numberOfSections; + for (NSInteger s = 0; s < sectionCount; s++) { + if ([_tableView numberOfRowsInSection:s] > 0) { + return [NSIndexPath indexPathForRow:0 inSection:s]; + } + } + return nil; +} + +- (nullable NSIndexPath *)lastIndexPathInTableView +{ + NSInteger lastSectionWithAnyRows = _tableView.numberOfSections; + NSInteger rowCount = 0; + while (rowCount == 0) { + lastSectionWithAnyRows -= 1; + if (lastSectionWithAnyRows < 0) { + return nil; + } + rowCount = [_tableView numberOfRowsInSection:lastSectionWithAnyRows]; + } + return [NSIndexPath indexPathForRow:rowCount - 1 inSection:lastSectionWithAnyRows]; +} + +// Same as valueForKeyPath:@"@min.self" but faster ++ (nullable id)fastArrayMin:(NSArray *)array ASDISPLAYNODE_CONST +{ + id min = nil; + for (id obj in array) { + if (min == nil || [obj compare:min] == NSOrderedAscending) { + min = obj; + } + } + return min; +} + +// Same as valueForKeyPath:@"@max.self" but faster ++ (nullable id)fastArrayMax:(NSArray *)array ASDISPLAYNODE_CONST +{ + id max = nil; + for (id obj in array) { + if (max == nil || [max compare:obj] == NSOrderedAscending) { + max = obj; + } + } + return max; +} + ++ (CGRect)initialSearchRectDownward:(BOOL)downward targetY:(CGFloat)targetY ASDISPLAYNODE_CONST +{ + static CGFloat const kInitialRowSearchHeight = 100; + CGRect result = CGRectMake(0, targetY, 1, kInitialRowSearchHeight); + if (!downward) { + result.origin.y -= result.size.height; + } + return result; +} + ++ (CGRect)growSearchRect:(CGRect)searchRect downward:(BOOL)downward ASDISPLAYNODE_CONST +{ + CGRect result = searchRect; + if (!downward) { + result.origin.y -= result.size.height; + } + result.size.height *= 2; + return result; +} + +@end diff --git a/Base/ASBaseDefines.h b/Base/ASBaseDefines.h index 96fc0355c3..53439810b6 100755 --- a/Base/ASBaseDefines.h +++ b/Base/ASBaseDefines.h @@ -70,6 +70,14 @@ # endif #endif +#ifndef ASDISPLAYNODE_CONST +# if ASDISPLAYNODE_GNUC (3, 0) +# define ASDISPLAYNODE_CONST __attribute__ ((const)) +# else +# define ASDISPLAYNODE_CONST /* no const */ +# endif +#endif + #ifndef ASDISPLAYNODE_WARN_UNUSED # if ASDISPLAYNODE_GNUC (3, 4) # define ASDISPLAYNODE_WARN_UNUSED __attribute__ ((warn_unused_result))