From e46c13edc549db65b9575fc16ffb5d2af976fc83 Mon Sep 17 00:00:00 2001 From: Rene Cacheaux Date: Sun, 21 Jun 2015 11:45:25 -0500 Subject: [PATCH] Exposes ASCollectionView's ability to determine scroll direction. Scroll direction detection can now support 2-axis collection view layouts. ASCollectionView can now determine scrollable axes. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 16 +++++- AsyncDisplayKit/ASCollectionView.h | 14 +++++ AsyncDisplayKit/ASCollectionView.mm | 54 +++++++++++++++---- AsyncDisplayKit/Details/ASScrollDirection.h | 12 +++++ AsyncDisplayKit/Details/ASScrollDirection.m | 20 +++++++ .../UICollectionViewLayout+ASConvenience.h | 15 ++++++ .../UICollectionViewLayout+ASConvenience.m | 17 ++++++ 7 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASScrollDirection.m create mode 100644 AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.h create mode 100644 AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index d52c10162b..c32af2805c 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -142,6 +142,9 @@ 05F20AA41A15733C00DCA68A /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */; }; 204C979E1B362CB3002B1083 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 204C979D1B362CB3002B1083 /* Default-568h@2x.png */; }; + 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */; }; + 205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; }; 242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; }; 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; }; 291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A311A951715005ACEAA /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -318,7 +321,10 @@ 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASSnapshotTestCase.mm; sourceTree = ""; }; 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageProtocols.h; sourceTree = ""; }; 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = ""; }; - 204C979D1B362CB3002B1083 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + 204C979D1B362CB3002B1083 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; + 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionViewLayout+ASConvenience.h"; sourceTree = ""; }; + 205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionViewLayout+ASConvenience.m"; sourceTree = ""; }; + 205F0E111B371BD7007741D0 /* ASScrollDirection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollDirection.m; sourceTree = ""; }; 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = ""; }; 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; @@ -392,6 +398,7 @@ 057D02C01AC0A66700C7AC3C /* AsyncDisplayKitTestHost */ = { isa = PBXGroup; children = ( + 204C979D1B362CB3002B1083 /* Default-568h@2x.png */, 057D02C51AC0A66700C7AC3C /* AppDelegate.h */, 057D02C61AC0A66700C7AC3C /* AppDelegate.m */, 057D02C11AC0A66700C7AC3C /* Supporting Files */, @@ -412,7 +419,6 @@ 058D09A3195D04C000B7D73C = { isa = PBXGroup; children = ( - 204C979D1B362CB3002B1083 /* Default-568h@2x.png */, 058D09B1195D04C000B7D73C /* AsyncDisplayKit */, 058D09C5195D04C000B7D73C /* AsyncDisplayKitTests */, 058D09AE195D04C000B7D73C /* Frameworks */, @@ -565,6 +571,7 @@ 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */, 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */, 296A0A311A951715005ACEAA /* ASScrollDirection.h */, + 205F0E111B371BD7007741D0 /* ASScrollDirection.m */, 058D09EA195D050800B7D73C /* ASTextNodeCoreTextAdditions.h */, 058D09EB195D050800B7D73C /* ASTextNodeCoreTextAdditions.m */, 058D09EC195D050800B7D73C /* ASTextNodeRenderer.h */, @@ -580,6 +587,8 @@ 058D09F5195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.h */, 058D09F6195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m */, 058D09F7195D050800B7D73C /* Transactions */, + 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */, + 205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */, 058D09FF195D050800B7D73C /* UIView+ASConvenience.h */, 058D0A00195D050800B7D73C /* UIView+ASConvenience.m */, ); @@ -704,6 +713,7 @@ 058D0A66195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.h in Headers */, 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, + 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -920,6 +930,7 @@ 058D0A15195D050800B7D73C /* ASDisplayNodeExtras.mm in Sources */, 058D0A1F195D050800B7D73C /* ASTextNodeTextKitHelpers.mm in Sources */, 055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */, + 205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */, 464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */, 055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */, 058D0A1D195D050800B7D73C /* ASTextNodeRenderer.mm in Sources */, @@ -943,6 +954,7 @@ 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, 058D0A13195D050800B7D73C /* ASControlNode.m in Sources */, 058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */, + 205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */, 05A6D05B19D0EB64002DD95E /* ASDealloc2MainObject.m in Sources */, 058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */, 058D0A27195D050800B7D73C /* _ASPendingState.m in Sources */, diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index cb5bfd33dc..ec6fd3242e 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -131,6 +131,20 @@ */ - (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; +/** + * Determines collection view's current scroll direction. Supports 2-axis collection views. + * + * @returns a bitmask of ASScrollDirection values. + */ +- (ASScrollDirection)scrollDirection; + +/** + * Determines collection view's scrollable directions. + * + * @returns a bitmask of ASScrollDirection values. + */ +- (ASScrollDirection)scrollableDirections; + @end diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 377804e038..ae5f799566 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -14,6 +14,7 @@ #import "ASDataController.h" #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" +#import "UICollectionViewLayout+ASConvenience.h" const static NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone; @@ -141,7 +142,7 @@ static BOOL _isInterceptedSelector(SEL sel) // https://github.com/facebook/AsyncDisplayKit/issues/385 asyncDataFetchingEnabled = NO; - ASDisplayNodeAssert([layout isKindOfClass:UICollectionViewFlowLayout.class], @"only flow layouts are currently supported"); + ASDisplayNodeAssert([layout asdk_isFlowLayout], @"only flow layouts are currently supported"); ASFlowLayoutDirection direction = (((UICollectionViewFlowLayout *)layout).scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASFlowLayoutDirectionHorizontal : ASFlowLayoutDirectionVertical; _layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:direction]; @@ -369,24 +370,57 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASScrollDirection)scrollDirection { CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview]; + return [self scrollDirectionForVelocity:scrollVelocity]; +} + +- (ASScrollDirection)scrollDirectionForVelocity:(CGPoint)scrollVelocity +{ ASScrollDirection direction = ASScrollDirectionNone; - if (_layoutController.layoutDirection == ASFlowLayoutDirectionHorizontal) { + ASScrollDirection scrollableDirections = [self scrollableDirections]; + + if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { // Can scroll horizontally. if (scrollVelocity.x > 0) { - direction = ASScrollDirectionRight; - } else if (scrollVelocity.x < 0) { - direction = ASScrollDirectionLeft; - } - } else { - if (scrollVelocity.y > 0) { - direction = ASScrollDirectionDown; + direction |= ASScrollDirectionRight; } else { - direction = ASScrollDirectionUp; + direction |= ASScrollDirectionLeft; + } + } + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { // Can scroll vertically. + if (scrollVelocity.y > 0) { + direction |= ASScrollDirectionDown; + } else { + direction |= ASScrollDirectionUp; } } return direction; } +- (ASScrollDirection)scrollableDirections +{ + if ([self.collectionViewLayout asdk_isFlowLayout]) { + return [self flowLayoutScrollableDirections:(UICollectionViewFlowLayout *)self.collectionViewLayout]; + } else { + return [self nonFlowLayoutScrollableDirections]; + } +} + +- (ASScrollDirection)flowLayoutScrollableDirections:(UICollectionViewFlowLayout *)flowLayout { + return (flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASScrollDirectionHorizontalDirections : ASScrollDirectionVerticalDirections; +} + +- (ASScrollDirection)nonFlowLayoutScrollableDirections +{ + ASScrollDirection scrollableDirection = ASScrollDirectionNone; + if (self.contentSize.width > self.bounds.size.width) { // Can scroll horizontally. + scrollableDirection |= ASScrollDirectionHorizontalDirections; + } + if (self.contentSize.height > self.bounds.size.height) { // Can scroll vertically. + scrollableDirection |= ASScrollDirectionVerticalDirections; + } + return scrollableDirection; +} + - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { [_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection]; diff --git a/AsyncDisplayKit/Details/ASScrollDirection.h b/AsyncDisplayKit/Details/ASScrollDirection.h index 4983544881..66aebfe001 100644 --- a/AsyncDisplayKit/Details/ASScrollDirection.h +++ b/AsyncDisplayKit/Details/ASScrollDirection.h @@ -8,6 +8,8 @@ #import +#import "ASBaseDefines.h" + typedef NS_OPTIONS(NSInteger, ASScrollDirection) { ASScrollDirectionNone = 0, ASScrollDirectionRight = 1 << 0, @@ -15,3 +17,13 @@ typedef NS_OPTIONS(NSInteger, ASScrollDirection) { ASScrollDirectionUp = 1 << 2, ASScrollDirectionDown = 1 << 3 }; + +extern const ASScrollDirection ASScrollDirectionHorizontalDirections; +extern const ASScrollDirection ASScrollDirectionVerticalDirections; + +ASDISPLAYNODE_EXTERN_C_BEGIN + +BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection); +BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection); + +ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKit/Details/ASScrollDirection.m b/AsyncDisplayKit/Details/ASScrollDirection.m new file mode 100644 index 0000000000..a228e2cb36 --- /dev/null +++ b/AsyncDisplayKit/Details/ASScrollDirection.m @@ -0,0 +1,20 @@ +/* Copyright (c) 2015-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 "ASScrollDirection.h" + +const ASScrollDirection ASScrollDirectionHorizontalDirections = ASScrollDirectionLeft | ASScrollDirectionRight; +const ASScrollDirection ASScrollDirectionVerticalDirections = ASScrollDirectionUp | ASScrollDirectionDown; + +BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionVerticalDirections) != 0; +} + +BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionHorizontalDirections) != 0; +} diff --git a/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.h b/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.h new file mode 100644 index 0000000000..1853be6fb7 --- /dev/null +++ b/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2015-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 + +@interface UICollectionViewLayout (ASConvenience) + +- (BOOL)asdk_isFlowLayout; + +@end diff --git a/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.m b/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.m new file mode 100644 index 0000000000..a401ec549e --- /dev/null +++ b/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.m @@ -0,0 +1,17 @@ +/* Copyright (c) 2015-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 "UICollectionViewLayout+ASConvenience.h" + +@implementation UICollectionViewLayout (ASConvenience) + +- (BOOL)asdk_isFlowLayout { + return [self isKindOfClass:UICollectionViewFlowLayout.class]; +} + +@end