Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delegate integration for custom scroll events #1

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
927A97F41736CA0E00539F5E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 927A97F31736CA0E00539F5E /* QuartzCore.framework */; };
AB0E97C7161B4BEC005498A0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0E97C6161B4BEC005498A0 /* UIKit.framework */; };
AB0E97C9161B4BEC005498A0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0E97C8161B4BEC005498A0 /* Foundation.framework */; };
AB0E97CB161B4BEC005498A0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0E97CA161B4BEC005498A0 /* CoreGraphics.framework */; };
Expand All @@ -25,6 +26,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
927A97F31736CA0E00539F5E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
AB0E97C2161B4BEC005498A0 /* LXRCVFL Example using Storyboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "LXRCVFL Example using Storyboard.app"; sourceTree = BUILT_PRODUCTS_DIR; };
AB0E97C6161B4BEC005498A0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
AB0E97C8161B4BEC005498A0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -55,6 +57,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
927A97F41736CA0E00539F5E /* QuartzCore.framework in Frameworks */,
AB0E97C7161B4BEC005498A0 /* UIKit.framework in Frameworks */,
AB0E97C9161B4BEC005498A0 /* Foundation.framework in Frameworks */,
AB0E97CB161B4BEC005498A0 /* CoreGraphics.framework in Frameworks */,
Expand Down Expand Up @@ -86,6 +89,7 @@
AB0E97C5161B4BEC005498A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
927A97F31736CA0E00539F5E /* QuartzCore.framework */,
AB0E97C6161B4BEC005498A0 /* UIKit.framework */,
AB0E97C8161B4BEC005498A0 /* Foundation.framework */,
AB0E97CA161B4BEC005498A0 /* CoreGraphics.framework */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#import <UIKit/UIKit.h>
#import "LXReorderableCollectionViewFlowLayout.h"

@interface LXCollectionViewController : UICollectionViewController <LXReorderableCollectionViewDatasource, LXReorderableCollectionViewDelegateFlowLayout>
@interface LXCollectionViewController : UICollectionViewController <LXReorderableCollectionViewDataSource, LXReorderableCollectionViewDelegateFlowLayout>

@property (strong, nonatomic) NSMutableArray *deck;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
return playingCardCell;
}

#pragma mark - LXReorderableCollectionViewDatasource methods
#pragma mark - LXReorderableCollectionViewDataSource methods

- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath {
PlayingCard *playingCard = [self.deck objectAtIndex:fromIndexPath.item];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@

@property (assign, nonatomic) CGFloat scrollingSpeed;
@property (assign, nonatomic) UIEdgeInsets scrollingTriggerEdgeInsets;
@property (weak, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;
@property (weak, nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer;
@property (strong, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;
@property (strong, nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer;

- (void)setUpGestureRecognizersOnCollectionView __attribute__((deprecated("Calls to setUpGestureRecognizersOnCollectionView method are not longer needed as setup are done automatically through KVO.")));

@end

@protocol LXReorderableCollectionViewDatasource <UICollectionViewDataSource>

- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath;
@protocol LXReorderableCollectionViewDataSource <UICollectionViewDataSource>

@optional

- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath;
- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath didMoveToIndexPath:(NSIndexPath *)toIndexPath;

- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath;

Expand All @@ -37,4 +38,7 @@
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath;
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath;

- (void)collectionViewScrollToNextPage:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout;
- (void)collectionViewScrollToPreviousPage:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#import "LXReorderableCollectionViewFlowLayout.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>

#define LX_FRAMES_PER_SECOND 60.0

Expand Down Expand Up @@ -73,6 +74,20 @@ - (CGPoint)LX_contentOffsetForPageIndex:(NSInteger)pageIndex {

@end

@interface CADisplayLink (LX_userInfo)
@property (nonatomic, copy) NSDictionary *LX_userInfo;
@end

@implementation CADisplayLink (LX_userInfo)
- (void) setLX_userInfo:(NSDictionary *) LX_userInfo {
objc_setAssociatedObject(self, "LX_userInfo", LX_userInfo, OBJC_ASSOCIATION_COPY);
}

- (NSDictionary *) LX_userInfo {
return objc_getAssociatedObject(self, "LX_userInfo");
}
@end

@interface UICollectionViewCell (LXReorderableCollectionViewFlowLayout)

- (UIImage *)LX_rasterizedImage;
Expand All @@ -97,9 +112,9 @@ @interface LXReorderableCollectionViewFlowLayout ()
@property (strong, nonatomic) UIView *currentView;
@property (assign, nonatomic) CGPoint currentViewCenter;
@property (assign, nonatomic) CGPoint panTranslationInCollectionView;
@property (strong, nonatomic) NSTimer *scrollingTimer;
@property (strong, nonatomic) CADisplayLink *displayLink;

@property (assign, nonatomic, readonly) id<LXReorderableCollectionViewDatasource> dataSource;
@property (assign, nonatomic, readonly) id<LXReorderableCollectionViewDataSource> dataSource;
@property (assign, nonatomic, readonly) id<LXReorderableCollectionViewDelegateFlowLayout> delegate;

@end
Expand All @@ -114,25 +129,24 @@ - (void)setDefaults {
}

- (void)setupCollectionView {
UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:@selector(handleLongPressGesture:)];
longPressGestureRecognizer.delegate = self;
[self.collectionView addGestureRecognizer:longPressGestureRecognizer];
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:@selector(handleLongPressGesture:)];
_longPressGestureRecognizer.delegate = self;

// Links the default long press gesture recognizer to the custom long press gesture recognizer we are creating now
// by enforcing failure dependency so that they doesn't clash.
for (UIGestureRecognizer *gestureRecognizer in self.collectionView.gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
[gestureRecognizer requireGestureRecognizerToFail:longPressGestureRecognizer];
[gestureRecognizer requireGestureRecognizerToFail:_longPressGestureRecognizer];
}
}
_longPressGestureRecognizer = longPressGestureRecognizer;

UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handlePanGesture:)];
panGestureRecognizer.delegate = self;
[self.collectionView addGestureRecognizer:panGestureRecognizer];
_panGestureRecognizer = panGestureRecognizer;
[self.collectionView addGestureRecognizer:_longPressGestureRecognizer];

_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handlePanGesture:)];
_panGestureRecognizer.delegate = self;
[self.collectionView addGestureRecognizer:_panGestureRecognizer];
}

- (id)init {
Expand Down Expand Up @@ -164,8 +178,8 @@ - (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttribut
}
}

- (id<LXReorderableCollectionViewDatasource>)dataSource {
return (id<LXReorderableCollectionViewDatasource>)self.collectionView.dataSource;
- (id<LXReorderableCollectionViewDataSource>)dataSource {
return (id<LXReorderableCollectionViewDataSource>)self.collectionView.dataSource;
}

- (id<LXReorderableCollectionViewDelegateFlowLayout>)delegate {
Expand All @@ -188,23 +202,30 @@ - (void)invalidateLayoutIfNecessary {

self.selectedItemIndexPath = newIndexPath;

[self.dataSource collectionView:self.collectionView itemAtIndexPath:previousIndexPath willMoveToIndexPath:newIndexPath];

if ([self.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:willMoveToIndexPath:)]) {
[self.dataSource collectionView:self.collectionView itemAtIndexPath:previousIndexPath willMoveToIndexPath:newIndexPath];
}

__weak typeof(self) weakSelf = self;
[self.collectionView performBatchUpdates:^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.collectionView deleteItemsAtIndexPaths:@[ previousIndexPath ]];
[strongSelf.collectionView insertItemsAtIndexPaths:@[ newIndexPath ]];
}
} completion:nil];
} completion:^(BOOL finished) {
__strong typeof(self) strongSelf = weakSelf;
if ([strongSelf.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:didMoveToIndexPath:)]) {
[strongSelf.dataSource collectionView:strongSelf.collectionView itemAtIndexPath:previousIndexPath didMoveToIndexPath:newIndexPath];
}
}];
}

- (void)invalidatesScrollTimer {
if (self.scrollingTimer.isValid) {
[self.scrollingTimer invalidate];
if (!self.displayLink.paused) {
[self.displayLink invalidate];
}
self.scrollingTimer = nil;
self.displayLink = nil;
}

- (void)scrollIfNecessary {
Expand Down Expand Up @@ -271,11 +292,17 @@ - (void)scrollWithDirection:(LXScrollingDirection)direction {
switch(direction) {
case LXScrollingDirectionUp:
case LXScrollingDirectionLeft: {
[self scrollToPreviousPage];
if([self.delegate respondsToSelector:@selector(collectionViewScrollToPreviousPage:layout:)])
[self.delegate collectionViewScrollToPreviousPage:self.collectionView layout:self];
else
[self scrollToPreviousPage];
} break;
case LXScrollingDirectionDown:
case LXScrollingDirectionRight: {
[self scrollToNextPage];
if([self.delegate respondsToSelector:@selector(collectionViewScrollToNextPage:layout:)])
[self.delegate collectionViewScrollToNextPage:self.collectionView layout:self];
else
[self scrollToNextPage];
} break;
default: {
// Do nothing...
Expand All @@ -287,28 +314,27 @@ - (void)scrollWithDirection:(LXScrollingDirection)direction {
}

- (void)setupScrollTimerInDirection:(LXScrollingDirection)direction {
if (self.scrollingTimer.isValid) {
LXScrollingDirection oldDirection = [self.scrollingTimer.userInfo[kLXScrollingDirectionKey] integerValue];
if (!self.displayLink.paused) {
LXScrollingDirection oldDirection = [self.displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue];

if (direction == oldDirection) {
return;
}
}

[self invalidatesScrollTimer];

self.scrollingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / LX_FRAMES_PER_SECOND
target:self
selector:@selector(handleScroll:)
userInfo:@{ kLXScrollingDirectionKey : @(direction) }
repeats:YES];

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScroll:)];
self.displayLink.LX_userInfo = @{ kLXScrollingDirectionKey : @(direction) };

[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

#pragma mark - Target/Action methods

// Tight loop, allocate memory sparely, even if they are stack allocation.
- (void)handleScroll:(NSTimer *)timer {
LXScrollingDirection direction = (LXScrollingDirection)[timer.userInfo[kLXScrollingDirectionKey] integerValue];
- (void)handleScroll:(CADisplayLink *)displayLink {
LXScrollingDirection direction = (LXScrollingDirection)[displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue];
if (direction == LXScrollingDirectionUnknown) {
return;
}
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ Credits
- Refactored by [Luke Scott](https://github.com/lukescott), with some help from [mulle-nat's fork](https://github.com/mulle-nat/LXReorderableCollectionViewFlowLayout).
- Playing cards in the demo are downloaded from [http://www.jfitz.com/cards/](http://www.jfitz.com/cards/).

Alternatives
============

- [DraggableCollectionView](https://github.com/lukescott/DraggableCollectionView) by [Luke Scott](https://github.com/lukescott).

License
=======

LXReorderableCollectionViewFlowLayout is available under the MIT license.
LXReorderableCollectionViewFlowLayout is available under [the MIT license](LICENSE).