diff --git a/DraggableCollectionView/Helpers/LSCollectionViewHelper.m b/DraggableCollectionView/Helpers/LSCollectionViewHelper.m index 9075b07..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; @@ -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: { @@ -234,16 +257,42 @@ - (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; - [self.collectionView addSubview:mockCell]; - [UIView - animateWithDuration:0.3 - animations:^{ - mockCell.transform = CGAffineTransformMakeScale(1.1f, 1.1f); - } - completion:nil]; + // 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]; + + 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; @@ -274,8 +323,19 @@ - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender [UIView animateWithDuration:0.3 animations:^{ - mockCell.center = layoutAttributes.center; - mockCell.transform = CGAffineTransformMakeScale(1.f, 1.f); + mockCell.center = [self convertPointToRootView:layoutAttributes.center]; + + 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]; @@ -317,17 +377,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 { @@ -336,10 +397,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]; @@ -404,12 +465,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 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