From 2363b19eb4b213bca0ed965ccd0c3660eda27fba Mon Sep 17 00:00:00 2001 From: "lancy.fernandes" Date: Fri, 22 Nov 2013 09:26:18 +0530 Subject: [PATCH 1/3] Issue : When the dragging is initiated on the empty area the closest cell is matched. Fix : Need to find the exact cell. --- .../Helpers/LSCollectionViewHelper.m | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m index 9075b07..bf5e0c3 100644 --- a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m +++ b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m @@ -209,6 +209,29 @@ - (NSIndexPath *)indexPathForItemClosestToPoint:(CGPoint)point return indexPath; } +- (NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point +{ + NSArray *layoutAttrsInRect; + NSIndexPath *indexPath; + NSIndexPath *toIndexPath = self.layoutHelper.toIndexPath; + + // We need original positions of cells + self.layoutHelper.toIndexPath = nil; + layoutAttrsInRect = [self.collectionView.collectionViewLayout layoutAttributesForElementsInRect:self.collectionView.bounds]; + self.layoutHelper.toIndexPath = toIndexPath; + + // What cell are we closest to? + for (UICollectionViewLayoutAttributes *layoutAttr in layoutAttrsInRect) + { + if (CGRectContainsPoint(layoutAttr.frame, point)) + { + indexPath = layoutAttr.indexPath; + } + } + + return indexPath; +} + - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateChanged) { @@ -218,7 +241,7 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender return; } - NSIndexPath *indexPath = [self indexPathForItemClosestToPoint:[sender locationInView:self.collectionView]]; + NSIndexPath *indexPath = [self indexPathForItemAtPoint:[sender locationInView:self.collectionView]]; switch (sender.state) { case UIGestureRecognizerStateBegan: { From 73a0670690681b512b7e25a13db1e9425cc4453a Mon Sep 17 00:00:00 2001 From: "lancy.fernandes" Date: Fri, 22 Nov 2013 09:45:25 +0530 Subject: [PATCH 2/3] Issue: Let us consider the collection view is just the bottom half of the window. Lets say user wants to drag the last cell to the first , say 100 cell to the 1. User initiates the dragging and moves the cell to the top of the collection view. As the collection view is just the bottom part of the window the mock cell gets clipped. Resolution : Add mock cell to the window. --- .../Helpers/LSCollectionViewHelper.m | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m index bf5e0c3..d7e4d3b 100644 --- a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m +++ b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m @@ -260,7 +260,12 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender mockCell = [[UIImageView alloc] initWithFrame:cell.frame]; mockCell.image = [self imageFromCell:cell]; mockCenter = mockCell.center; - [self.collectionView addSubview:mockCell]; + // Adding the mock cell to the root window so that if the collection view is not full screen, + // it is not clipped by the collection view when dragging. + [[self rootView] addSubview:mockCell]; + + mockCell.center = [self convertPointToRootView:mockCenter]; + [UIView animateWithDuration:0.3 animations:^{ @@ -297,7 +302,7 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender [UIView animateWithDuration:0.3 animations:^{ - mockCell.center = layoutAttributes.center; + mockCell.center = [self convertPointToRootView:layoutAttributes.center]; mockCell.transform = CGAffineTransformMakeScale(1.f, 1.f); } completion:^(BOOL finished) { @@ -340,17 +345,18 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)sender if(sender.state == UIGestureRecognizerStateChanged) { // Move mock to match finger fingerTranslation = [sender translationInView:self.collectionView]; - mockCell.center = _CGPointAdd(mockCenter, fingerTranslation); + CGPoint center = _CGPointAdd(mockCenter, fingerTranslation); + mockCell.center = [self convertPointToRootView:center]; // Scroll when necessary if (canScroll) { UICollectionViewFlowLayout *scrollLayout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout; if([scrollLayout scrollDirection] == UICollectionViewScrollDirectionVertical) { - if (mockCell.center.y < (CGRectGetMinY(self.collectionView.bounds) + self.scrollingEdgeInsets.top)) { + if (center.y < (CGRectGetMinY(self.collectionView.bounds) + self.scrollingEdgeInsets.top)) { [self setupScrollTimerInDirection:_ScrollingDirectionUp]; } else { - if (mockCell.center.y > (CGRectGetMaxY(self.collectionView.bounds) - self.scrollingEdgeInsets.bottom)) { + if (center.y > (CGRectGetMaxY(self.collectionView.bounds) - self.scrollingEdgeInsets.bottom)) { [self setupScrollTimerInDirection:_ScrollingDirectionDown]; } else { @@ -359,10 +365,10 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)sender } } else { - if (mockCell.center.x < (CGRectGetMinX(self.collectionView.bounds) + self.scrollingEdgeInsets.left)) { + if (center.x < (CGRectGetMinX(self.collectionView.bounds) + self.scrollingEdgeInsets.left)) { [self setupScrollTimerInDirection:_ScrollingDirectionLeft]; } else { - if (mockCell.center.x > (CGRectGetMaxX(self.collectionView.bounds) - self.scrollingEdgeInsets.right)) { + if (center.x > (CGRectGetMaxX(self.collectionView.bounds) - self.scrollingEdgeInsets.right)) { [self setupScrollTimerInDirection:_ScrollingDirectionRight]; } else { [self invalidatesScrollTimer]; @@ -427,12 +433,28 @@ - (void)handleScroll:(NSTimer *)timer { } mockCenter = _CGPointAdd(mockCenter, translation); - mockCell.center = _CGPointAdd(mockCenter, fingerTranslation); + CGPoint center = _CGPointAdd(mockCenter, fingerTranslation); self.collectionView.contentOffset = _CGPointAdd(contentOffset, translation); // Warp items while scrolling - NSIndexPath *indexPath = [self indexPathForItemClosestToPoint:mockCell.center]; + NSIndexPath *indexPath = [self indexPathForItemClosestToPoint:center]; [self warpToIndexPath:indexPath]; } +- (CGPoint)convertPointToRootView:(CGPoint)point +{ + return [self.collectionView convertPoint:point toView:[self rootView]]; +} + +- (CGPoint)convertPointFromRootView:(CGPoint)point +{ + return [self.collectionView convertPoint:point fromView:[self rootView]]; +} + +- (UIView *)rootView +{ + UIWindow *window = [[UIApplication sharedApplication] keyWindow]; + return window.subviews[0]; +} + @end From be2a8eec47a625851a0a5bcc225a233b5a22b006 Mon Sep 17 00:00:00 2001 From: "lancy.fernandes" Date: Fri, 22 Nov 2013 10:59:36 +0530 Subject: [PATCH 3/3] Issue: Applying transform for the mock cell blurs the view. Resolution: Provided the optional delegate method so user can provide his/her own view and also changing properties when needed like font colour or background color. --- .../Helpers/LSCollectionViewHelper.m | 52 +++++++++++++++---- .../UICollectionViewDataSource_Draggable.h | 5 ++ FlowLayoutDemo/ViewController.m | 13 +++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m index d7e4d3b..6cf7e1b 100644 --- a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m +++ b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m @@ -30,7 +30,7 @@ typedef NS_ENUM(NSInteger, _ScrollingDirection) { @interface LSCollectionViewHelper () { NSIndexPath *lastIndexPath; - UIImageView *mockCell; + UIView *mockCell; CGPoint mockCenter; CGPoint fingerTranslation; CADisplayLink *timer; @@ -257,8 +257,17 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; cell.highlighted = NO; [mockCell removeFromSuperview]; - mockCell = [[UIImageView alloc] initWithFrame:cell.frame]; - mockCell.image = [self imageFromCell:cell]; + if([self.collectionView.dataSource respondsToSelector:@selector(collectionView:viewForDraggingAtIndexPath:)]) + { + mockCell = [(id)self.collectionView.dataSource + collectionView:self.collectionView viewForDraggingAtIndexPath:indexPath]; + mockCell.center = cell.center; + } + else + { + mockCell = [[UIImageView alloc] initWithFrame:cell.frame]; + ((UIImageView *)mockCell).image = [self imageFromCell:cell]; + } mockCenter = mockCell.center; // Adding the mock cell to the root window so that if the collection view is not full screen, // it is not clipped by the collection view when dragging. @@ -266,12 +275,24 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender mockCell.center = [self convertPointToRootView:mockCenter]; - [UIView - animateWithDuration:0.3 - animations:^{ - mockCell.transform = CGAffineTransformMakeScale(1.1f, 1.1f); - } - completion:nil]; + if([mockCell isKindOfClass:[UIImageView class]]) + { + [UIView + animateWithDuration:0.3 + animations:^{ + mockCell.transform = CGAffineTransformMakeScale(1.1f, 1.1f); + } + completion:nil]; + } + else + { + mockCell.transform = CGAffineTransformMakeScale((cell.bounds.size.width/mockCell.bounds.size.width), + (cell.bounds.size.height/mockCell.bounds.size.height)); + [UIView animateWithDuration:0.3f + animations:^{ + mockCell.transform = CGAffineTransformIdentity; + }]; + } // Start warping lastIndexPath = indexPath; @@ -303,7 +324,18 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender animateWithDuration:0.3 animations:^{ mockCell.center = [self convertPointToRootView:layoutAttributes.center]; - mockCell.transform = CGAffineTransformMakeScale(1.f, 1.f); + + CGAffineTransform transform = CGAffineTransformIdentity; + if([mockCell isKindOfClass:[UIImageView class]]) + { + transform = CGAffineTransformMakeScale(1.f, 1.f); + } + else + { + transform = CGAffineTransformMakeScale((layoutAttributes.frame.size.width/mockCell.bounds.size.width), + (layoutAttributes.frame.size.height/mockCell.bounds.size.height)); + } + mockCell.transform = transform; } completion:^(BOOL finished) { [mockCell removeFromSuperview]; diff --git a/DraggableCollectionView/UICollectionViewDataSource_Draggable.h b/DraggableCollectionView/UICollectionViewDataSource_Draggable.h index 7e34bca..afc9dbc 100644 --- a/DraggableCollectionView/UICollectionViewDataSource_Draggable.h +++ b/DraggableCollectionView/UICollectionViewDataSource_Draggable.h @@ -18,4 +18,9 @@ - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)toIndexPath; +// When dragging by default a cell copy (image) is created and tranform is applied. +// If you think the image looks blur when transform is applied, you can provide your own view by invoking this method. +// This method assumes that your view big enough so no transform is applied. +- (UIView *)collectionView:(UICollectionView *)collectionView viewForDraggingAtIndexPath:(NSIndexPath *)indexPath; + @end diff --git a/FlowLayoutDemo/ViewController.m b/FlowLayoutDemo/ViewController.m index 22705a3..fc55182 100644 --- a/FlowLayoutDemo/ViewController.m +++ b/FlowLayoutDemo/ViewController.m @@ -76,4 +76,17 @@ - (void)collectionView:(LSCollectionViewHelper *)collectionView moveItemAtIndexP [data2 insertObject:index atIndex:toIndexPath.item]; } +- (UIView *)collectionView:(UICollectionView *)collectionView viewForDraggingAtIndexPath:(NSIndexPath *)indexPath +{ + NSMutableArray *data = [sections objectAtIndex:indexPath.section]; + NSString *text = [data objectAtIndex:indexPath.item]; + + UILabel *draggingView = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 200.0f)]; + draggingView.backgroundColor = [UIColor lightGrayColor]; + draggingView.font = [draggingView.font fontWithSize:draggingView.font.pointSize + 16.0f]; + draggingView.textAlignment = NSTextAlignmentCenter; + draggingView.text = text; + return draggingView; +} + @end