Skip to content

Instantly share code, notes, and snippets.

@leeprobert
Last active December 18, 2015 19:09
Show Gist options
  • Select an option

  • Save leeprobert/5831377 to your computer and use it in GitHub Desktop.

Select an option

Save leeprobert/5831377 to your computer and use it in GitHub Desktop.
UIView+DragAndDrop is an iOS Category for enabling simple drag & drop functionality on any UIView
//
// UIView+DragAndDrop.h
// Engager
//
// Created by Lee Probert on 21/06/2013.
// Copyright (c) 2013 Agnitio. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol UIViewDragAndDropDelegate;
extern const NSString *notificationViewDidStartDragging;
extern const NSString *notificationViewDidUpdateDragging;
extern const NSString *notificationViewDidEndDragging;
@interface UIView (DragAndDrop)
@property (nonatomic, strong) UILongPressGestureRecognizer *viewDragGestureRecognizer;
@property (nonatomic, assign) id<UIViewDragAndDropDelegate> dragDropDelegate;
@property (nonatomic, assign, getter=isEnableDragAndDrop) BOOL enableDragAndDrop;
@property (nonatomic, assign, getter=isEnableSpringBack) BOOL enableSpringBack;
@property (nonatomic, assign, getter=isEnableShrinkDrop) BOOL enableShrinkDrop;
@property (nonatomic, assign, getter=isDraggingThumb) BOOL draggingThumb;
@property (nonatomic, strong) UIImageView *thumbView;
@property (nonatomic, assign) CGPoint startDragPoint;
-(IBAction)viewDragDaropAction:(id)sender;
-(void) configureDragAndDrop;
-(void) startDraggingAtPoint:(CGPoint)point;
-(void) endDraggingAtPoint:(CGPoint)point;
-(void) updateDraggingAtPoint:(CGPoint)point;
-(CGPoint) getThumbPointFromPoint:(CGPoint)point targetView:(UIView*)view;
-(UIView*) setNewThumbFrameFromPoint:(CGPoint)point;
-(NSDictionary*) getNotificationUserInfoDictionary;
-(UIImage*) getThumbImage;
-(void) removeDragDropThumbView;
-(void) animateDropEffect;
@end
@protocol UIViewDragAndDropDelegate <NSObject>
@optional
-(void) view:(UIView*)dragView didStartDraggingWithGesture:(UIGestureRecognizer*)gesture inView:(UIView*)containerView atPoint:(CGPoint)point;
-(void) view:(UIView*)dragView didUpdateDraggingWithGesture:(UIGestureRecognizer*)gesture inView:(UIView*)containerView atPoint:(CGPoint)point;
-(void) view:(UIView*)dragView didEndDraggingWithGesture:(UIGestureRecognizer*)gesture inView:(UIView*)containerView atPoint:(CGPoint)point;
@end
//
// UIView+DragAndDrop.m
// Engager
//
// Created by Lee Probert on 21/06/2013.
// Copyright (c) 2013 Agnitio. All rights reserved.
//
#import "UIView+DragAndDrop.h"
#import "AppDelegate.h"
#import <objc/runtime.h>
static char const * const _viewDragGestureRecognizer = "_viewDragGestureRecognizer";
static char const * const _dragDropDelegate = "_dragDropDelegate";
static char const * const _enableDragAndDrop = "_enableDragAndDrop";
static char const * const _enableSpringBack = "_enableSpringBack";
static char const * const _enableShrinkDrop = "_enableShrinkDrop";
static char const * const _draggingThumb = "_draggingThumb";
static char const * const _thumbView = "_thumbView";
static char const * const _startDragPointX = "_startDragPointX";
static char const * const _startDragPointY = "_startDragPointY";
NSString *notificationViewDidStartDragging = @"notificationViewDidStartDragging";
NSString *notificationViewDidUpdateDragging = @"notificationViewDidUpdateDragging";
NSString *notificationViewDidEndDragging = @"notificationViewDidEndDragging";
@implementation UIView (DragAndDrop)
#pragma mark - setter & getter pairs
-(void) setViewDragGestureRecognizer:(UILongPressGestureRecognizer *)viewDragGestureRecognizer {
objc_setAssociatedObject(self, _viewDragGestureRecognizer, viewDragGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(UILongPressGestureRecognizer*) viewDragGestureRecognizer {
return objc_getAssociatedObject(self, _viewDragGestureRecognizer);
}
-(void) setDragDropDelegate:(id<UIViewDragAndDropDelegate>)dragDropDelegate {
objc_setAssociatedObject(self, _dragDropDelegate, dragDropDelegate, OBJC_ASSOCIATION_ASSIGN);
}
-(id) dragDropDelegate {
return objc_getAssociatedObject(self, _dragDropDelegate);
}
-(void) setEnableDragAndDrop:(BOOL)enableDragAndDrop {
objc_setAssociatedObject(self, _enableDragAndDrop, @(enableDragAndDrop), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if(enableDragAndDrop && !self.viewDragGestureRecognizer){
[self configureDragAndDrop];
}else{
// tear down
[self removeGestureRecognizer:self.viewDragGestureRecognizer];
self.viewDragGestureRecognizer = nil;
self.thumbView = nil;
}
}
-(BOOL) isEnableDragAndDrop {
NSNumber *b = objc_getAssociatedObject(self, _enableDragAndDrop);
return [b boolValue];
}
-(void) setEnableSpringBack:(BOOL)enableSpringBack {
objc_setAssociatedObject(self, _enableSpringBack, @(enableSpringBack), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL) isEnableSpringBack {
NSNumber *b = objc_getAssociatedObject(self, _enableSpringBack);
return [b boolValue];
}
-(void) setEnableShrinkDrop:(BOOL)enableShrinkDrop {
objc_setAssociatedObject(self, _enableShrinkDrop, @(enableShrinkDrop), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL) isEnableShrinkDrop {
NSNumber *b = objc_getAssociatedObject(self, _enableShrinkDrop);
return [b boolValue];
}
-(void) setDraggingThumb:(BOOL)draggingThumb {
objc_setAssociatedObject(self, _draggingThumb, @(draggingThumb), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL) isDraggingThumb {
NSNumber *b = objc_getAssociatedObject(self, _draggingThumb);
return [b boolValue];
}
-(void) setThumbView:(UIImageView *)thumbView {
objc_setAssociatedObject(self, _thumbView, thumbView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(UIImageView*) thumbView {
return objc_getAssociatedObject(self, _thumbView);
}
-(void) setStartDragPoint:(CGPoint)startDragPoint {
objc_setAssociatedObject(self, _startDragPointX, @(startDragPoint.x), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, _startDragPointY, @(startDragPoint.y), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(CGPoint) startDragPoint {
NSNumber *posX = objc_getAssociatedObject(self, _startDragPointX);
NSNumber *posY = objc_getAssociatedObject(self, _startDragPointY);
CGPoint pt = CGPointMake([posX floatValue], [posY floatValue]);
return pt;
}
#pragma mark - public
-(IBAction)viewDragDropAction:(id)sender {
UIGestureRecognizer *gesture = (UIGestureRecognizer*)sender;
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
[self startDraggingAtPoint:[gesture locationInView:self]];
break;
case UIGestureRecognizerStateChanged:
[self updateDraggingAtPoint:[gesture locationInView:self]];
break;
case UIGestureRecognizerStateEnded:
[self endDraggingAtPoint:[gesture locationInView:self]];
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
[self endDraggingAtPoint:[gesture locationInView:self]];
break;
default:
break;
}
}
#pragma mark - private
-(void) startDraggingAtPoint:(CGPoint)point; {
self.draggingThumb = YES;
self.startDragPoint = point;
self.enableSpringBack = YES; // always the default. Delegates can change if required at drag end.
self.enableShrinkDrop = NO; // default to spring back so shrink drop needs to be initiated by delegates
UIImage *thumb = [self getThumbImage];
self.thumbView = [[UIImageView alloc] initWithImage:thumb];
self.thumbView.backgroundColor = [UIColor clearColor];
self.thumbView.alpha = 0.8;
UIView *topView = [self setNewThumbFrameFromPoint:point];
[topView addSubview:self.thumbView];
[[NSNotificationCenter defaultCenter] postNotificationName:notificationViewDidStartDragging object:self userInfo:[self getNotificationUserInfoDictionary]];
if(self.dragDropDelegate && [self.dragDropDelegate respondsToSelector:@selector(view:didStartDraggingWithGesture:inView:atPoint:)]){
[self.dragDropDelegate view:self didStartDraggingWithGesture:self.viewDragGestureRecognizer inView:self.thumbView.superview atPoint:self.thumbView.frame.origin];
}
self.alpha = 0.2f;
}
-(void) updateDraggingAtPoint:(CGPoint)point; {
if(self.thumbView && self.thumbView.superview) {
[self setNewThumbFrameFromPoint:point];
[[NSNotificationCenter defaultCenter] postNotificationName:notificationViewDidUpdateDragging object:self userInfo:[self getNotificationUserInfoDictionary]];
if(self.dragDropDelegate && [self.dragDropDelegate respondsToSelector:@selector(view:didUpdateDraggingWithGesture:inView:atPoint:)]){
[self.dragDropDelegate view:self didUpdateDraggingWithGesture:self.viewDragGestureRecognizer inView:self.thumbView.superview atPoint:self.thumbView.frame.origin];
}
}
}
-(void) endDraggingAtPoint:(CGPoint)point; {
self.draggingThumb = NO;
self.alpha = 1.0f;
if(self.thumbView && self.thumbView.superview) {
[[NSNotificationCenter defaultCenter] postNotificationName:notificationViewDidEndDragging object:self userInfo:[self getNotificationUserInfoDictionary]];
if(self.dragDropDelegate && [self.dragDropDelegate respondsToSelector:@selector(view:didEndDraggingWithGesture:inView:atPoint:)]){
[self.dragDropDelegate view:self didEndDraggingWithGesture:self.viewDragGestureRecognizer inView:self.thumbView.superview atPoint:self.thumbView.frame.origin];
}
/*
The Delegates are given the chance to disable spring back so they can properly handle the drop behaviour
*/
if(self.isEnableSpringBack){
[UIView animateWithDuration:0.33f delay:0.0f options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGPoint p = [self convertPoint:CGPointZero toView:self.thumbView.superview];
self.thumbView.frame = CGRectMake(p.x, p.y, self.thumbView.frame.size.width, self.thumbView.frame.size.height);
} completion:^(BOOL finished) {
[self removeDragDropThumbView];
}];
}else{
if(self.isEnableShrinkDrop){
[self animateDropEffect];
}else{
[self removeDragDropThumbView];
}
}
}
}
-(CGPoint) getThumbPointFromPoint:(CGPoint)point targetView:(UIView*)view {
CGPoint convertedPoint = [self convertPoint:point toView:view];
CGPoint newPoint = CGPointMake(convertedPoint.x-self.startDragPoint.x, convertedPoint.y-self.startDragPoint.y);
return newPoint;
}
-(UIView*) setNewThumbFrameFromPoint:(CGPoint)point {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
UIView *topView = [[[appDelegate window] rootViewController] view];
CGPoint newPoint = [self getThumbPointFromPoint:point targetView:topView];
self.thumbView.frame = CGRectMake(newPoint.x, newPoint.y, self.thumbView.frame.size.width, self.thumbView.frame.size.height);
return topView;
}
-(NSDictionary*) getNotificationUserInfoDictionary {
NSDictionary *d = @{@"gesture" : self.viewDragGestureRecognizer,
@"view" : self.thumbView.superview,
@"pointX" : @(self.thumbView.frame.origin.x),
@"pointY" : @(self.thumbView.frame.origin.y)};
return d;
}
-(UIImage*) getThumbImage {
CGSize screenShotSize = self.bounds.size;
UIImage *img;
UIGraphicsBeginImageContextWithOptions(screenShotSize, NO, 2.0f);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
-(void) configureDragAndDrop {
self.viewDragGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(viewDragDropAction:)];
self.viewDragGestureRecognizer.minimumPressDuration = 0.33f;
self.viewDragGestureRecognizer.numberOfTouchesRequired = 1;
[self addGestureRecognizer:self.viewDragGestureRecognizer];
}
-(void) removeDragDropThumbView {
if(self.thumbView && self.thumbView.superview) {
[self.thumbView removeFromSuperview];
}
self.thumbView = nil;
}
-(void) animateDropEffect {
if(self.thumbView && self.thumbView.superview) {
[UIView animateWithDuration:0.33f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
self.thumbView.transform = CGAffineTransformScale(self.thumbView.transform, 0.0f, 0.0f);
} completion:^(BOOL finished) {
[self removeDragDropThumbView];
}];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment