title: How to Drag an Image with Gesture Recognition Xcode
body: <p>I have made a little test to drag views in a UIScrollView to UIView where I can call any action.</p>
  
  <p>I'm trying to be able to make the image I made in the UIScrollView Draggable. I have not been able to make this work.</p>
  
  <p>I was able to create test views which are not images that are able to drag.</p>
  
  <p><strong>TestViewController.h</strong></p>
  
  <pre><code>#import "JDDroppableView.h"
  
  @interface TestViewController : UIViewController &lt;JDDroppableViewDelegate&gt;
  {
  UIScrollView* mScrollView;
  UIView* mDropTarget;
  UIImageView *images;
  UIImageView *image;
  CGPoint mLastPosition;
  }
  
  - (void) relayout;
  - (void) addView: (id) sender;
  - (void) scrollToBottomAnimated: (BOOL) animated;
  @property (nonatomic, retain) IBOutlet UIImageView *images;
  @property (nonatomic, retain) IBOutlet UIImageView *image;
  @end
  </code></pre>
  
  <p><strong>TestViewController.m</strong></p>
  
  <pre><code>#import "TestViewController.h"
  #import "JDDroppableView.h"
  #import &lt;QuartzCore/QuartzCore.h&gt;
  
  // setup view vars
  static NSInteger sDROPVIEW_MARGIN = 3;
  static CGFloat   sCOUNT_OF_VIEWS_HORICONTALLY = 1.0;
  static CGFloat   sCOUNT_OF_VIEWS_VERTICALLY   = 1.0;
  
  @implementation TestViewController
  @synthesize images;
  @synthesize image;
  
  - (void)loadView
  {
  [super loadView];
  self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
  
  // increase viewcount on ipad
  if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
      sCOUNT_OF_VIEWS_HORICONTALLY = 0;
      sCOUNT_OF_VIEWS_VERTICALLY   = 0;
  }
  
  // add button
  UIButton* button = [UIButton buttonWithType: UIButtonTypeCustom];
  [button setImage:[UIImage imageNamed:@"002.jpg"] forState:UIControlStateNormal];
  button.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
  [button setTitle: @"+" forState: UIControlStateNormal];
  [button addTarget: self action: @selector(addView:) forControlEvents: UIControlEventTouchUpInside];
  button.backgroundColor = [UIColor colorWithRed: 0.75 green: 0.2 blue: 0 alpha: 1.0];
  button.layer.cornerRadius = 5.0;
  button.showsTouchWhenHighlighted = YES;
  button.adjustsImageWhenHighlighted = YES;
  button.frame = CGRectMake(20,
                            self.view.frame.size.height - 52,
                            self.view.frame.size.width - 40, // width
                            32); // height
  [self.view addSubview: button];
  
  // drop target
  mDropTarget = [[UIView alloc] init];
  mDropTarget.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  mDropTarget.backgroundColor = [UIColor orangeColor];
  mDropTarget.frame = CGRectMake(0, 0, 30, 30);
  mDropTarget.center = CGPointMake(self.view.frame.size.width/2, button.frame.origin.y - 50);
  mDropTarget.layer.cornerRadius = 15;
  [self.view addSubview: mDropTarget];
  [mDropTarget release];
  
  // scrollview
  mScrollView = [[UIScrollView alloc] init];
  mScrollView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  mScrollView.backgroundColor = [UIColor colorWithRed: 0.75 green: 0.2 blue: 0 alpha: 1.0];
  mScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
  mScrollView.scrollIndicatorInsets = UIEdgeInsetsMake(5, 5, 5, 5);
  mScrollView.contentInset = UIEdgeInsetsMake(6, 6, 6, 6);
  mScrollView.layer.cornerRadius = 5.0;
  mScrollView.frame = CGRectMake(20,20, self.view.frame.size.width - 40, mDropTarget.center.y - 70);
  mScrollView.userInteractionEnabled = NO;
  mScrollView.canCancelContentTouches = NO;
  
  [self.view addSubview: mScrollView];
  [mScrollView release];
  
  // animate some draggable views in
  int numberOfViews            = sCOUNT_OF_VIEWS_HORICONTALLY*floor(sCOUNT_OF_VIEWS_VERTICALLY) + 2;
  CGFloat animationTimePerView = 0.15;
  for (int i = 0; i &lt; numberOfViews; i++) {
      [self performSelector: @selector(addView:) withObject: nil afterDelay: i*animationTimePerView];
      if (i%(int)sCOUNT_OF_VIEWS_HORICONTALLY==0) {
          [self performSelector: @selector(scrollToBottomAnimated:) withObject: [NSNumber numberWithBool: YES] afterDelay: i*animationTimePerView];
      }
  }
  
  CGRect myImageRect = CGRectMake(0, 0, 320, 100);
  image = [[UIImageView alloc] initWithFrame:myImageRect];
  [image setImage:[UIImage imageNamed:@"002.jpg"]];
  [image setUserInteractionEnabled:YES];
  [self.view addSubview:image];
  [mScrollView addSubview:image];
  
  // reenable userinteraction after animation ended
  [mScrollView performSelector: @selector(setUserInteractionEnabled:) withObject: [NSNumber numberWithBool: YES] afterDelay: numberOfViews*animationTimePerView];
  }
  
  #pragma layout
  
  - (void) relayout
  {
  // cancel all animations
  [mScrollView.layer removeAllAnimations];
  for (UIView* subview in mScrollView.subviews)
      [subview.layer removeAllAnimations];
  
  // setup new animation
  [UIView beginAnimations: @"drag" context: nil];
  
  // init calculation vars
  float posx = 0;
  float posy = 0;
  CGRect frame = CGRectZero;
  mLastPosition = CGPointMake(0, -100);
  CGFloat contentWidth = mScrollView.contentSize.width - mScrollView.contentInset.left - mScrollView.contentInset.right;
  
  // iterate through all subviews
  for (UIView* subview in mScrollView.subviews)
  {
      // ignore scroll indicators
      if (!subview.userInteractionEnabled) {
          continue;
      }
  
      // create new position
      frame = subview.frame;
      frame.origin.x = posx;
      frame.origin.y = posy;
  
      // update frame (if it did change)
      if (frame.origin.x != subview.frame.origin.x ||
          frame.origin.y != subview.frame.origin.y) {
          subview.frame = frame;
      }
  
      // save last position
      mLastPosition = CGPointMake(posx, posy);
  
      // add size and margin
      posx += frame.size.width + sDROPVIEW_MARGIN;
  
      // goto next row if needed
      if (posx &gt; mScrollView.frame.size.width - mScrollView.contentInset.left - mScrollView.contentInset.right)
      {
          posx = 0;
          posy += frame.size.height + sDROPVIEW_MARGIN;
      }
  }
  
  // fix last posy for correct contentSize
  if (posx != 0) {
      posy += frame.size.height;
  } else {
      posy -= sDROPVIEW_MARGIN;
  }
  
  // update content size
  mScrollView.contentSize = CGSizeMake(contentWidth, posy);
  
  [UIView commitAnimations];
  }
  
  - (void) addView: (id) sender
  {
  CGFloat contentWidth  = mScrollView.frame.size.width  - mScrollView.contentInset.left - mScrollView.contentInset.right;
  CGFloat contentHeight = mScrollView.frame.size.height - mScrollView.contentInset.top;
  CGSize size = CGSizeMake(((contentWidth-sDROPVIEW_MARGIN*(sCOUNT_OF_VIEWS_HORICONTALLY-1))/sCOUNT_OF_VIEWS_HORICONTALLY),
                           floor((contentHeight-sDROPVIEW_MARGIN*(sCOUNT_OF_VIEWS_VERTICALLY-1))/sCOUNT_OF_VIEWS_VERTICALLY));
  
  JDDroppableView * dropview = [[JDDroppableView alloc] initWithDropTarget: mDropTarget];
  dropview.backgroundColor = [UIColor blackColor];
  dropview.layer.cornerRadius = 3.0;
  dropview.frame = CGRectMake(mLastPosition.x, mLastPosition.y, size.width, size.height);
  dropview.delegate = self;
  
  [mScrollView addSubview: dropview];
  [dropview release];
  
  [self relayout];
  
  // scroll to bottom, if added manually
  if ([sender isKindOfClass: [UIButton class]]) {
      [self scrollToBottomAnimated: YES];
  }
  }
  
  - (void) scrollToBottomAnimated: (BOOL) animated
  {
  [mScrollView.layer removeAllAnimations];
  
  CGFloat bottomScrollPosition = mScrollView.contentSize.height;
  bottomScrollPosition -= mScrollView.frame.size.height;
  bottomScrollPosition += mScrollView.contentInset.top;
  bottomScrollPosition = MAX(-mScrollView.contentInset.top,bottomScrollPosition);
  CGPoint newOffset = CGPointMake(-mScrollView.contentInset.left, bottomScrollPosition);
  if (newOffset.y != mScrollView.contentOffset.y) {
      [mScrollView setContentOffset: newOffset animated: animated];
  }
  }
  
  #pragma -
  #pragma droppabe view delegate
  
  - (BOOL) shouldAnimateDroppableViewBack: (JDDroppableView *)view wasDroppedOnTarget: (UIView *)target
  {
  [self droppableView: view leftTarget: target];
  
  CGRect frame = view.frame;
  frame.size.width *= 0.3;
  frame.size.height *= 0.3;
  frame.origin.x += (view.frame.size.width-frame.size.width)/2;
  frame.origin.y += (view.frame.size.height-frame.size.height)/2;
  
  [UIView beginAnimations: @"drag" context: nil];
  [UIView setAnimationDelegate: view];
  [UIView setAnimationDidStopSelector: @selector(removeFromSuperview)];
  
  view.frame = frame;
  view.center = target.center;
  
  [UIView commitAnimations];
  
  [self relayout];
  [mScrollView flashScrollIndicators];
  
  return NO;
  }
  
  - (void) droppableViewBeganDragging:(JDDroppableView *)view
  {
  [UIView beginAnimations: @"drag" context: nil];
  view.backgroundColor = [UIColor colorWithRed: 1 green: 0.5 blue: 0 alpha: 1];
  view.alpha = 0.8;
  [UIView commitAnimations];
  }
  
  - (void) droppableView:(JDDroppableView *)view enteredTarget:(UIView *)target
  {
  target.transform = CGAffineTransformMakeScale(1.5, 1.5);
  target.backgroundColor = [UIColor greenColor];
  //  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"image Collided"
  //                                                message:@"FuckYea"
  //                                                delegate:nil
  //                                      cancelButtonTitle:@"Reset!"
  //                                      otherButtonTitles:nil];
  //[alert show];
  CGRect myImageRect = CGRectMake(0, 0, 320, 100);
  images = [[UIImageView alloc] initWithFrame:myImageRect];
  [images setImage:[UIImage imageNamed:@"002.jpg"]];
  [self.view addSubview:images];
  }
  
  - (void) droppableView:(JDDroppableView *)view leftTarget:(UIView *)target
  {
  target.transform = CGAffineTransformMakeScale(1.0, 1.0);
  target.backgroundColor = [UIColor orangeColor];
  }
  
  - (void) droppableViewEndedDragging:(JDDroppableView *)view
  {
  [UIView beginAnimations: @"drag" context: nil];
  view.backgroundColor = [UIColor blackColor];
  view.alpha = 1.0;
  [UIView commitAnimations];
  }
  
  @end
  </code></pre>
  
  <p><strong>JDDroppableView.h</strong></p>
  
  <pre><code>@class JDDroppableView;
  
  @protocol JDDroppableViewDelegate &lt;NSObject&gt;
  @optional
  - (void) droppableViewBeganDragging: (JDDroppableView*) view;
  - (void) droppableViewDidMove: (JDDroppableView*) view;
  - (void) droppableView: (JDDroppableView*) view enteredTarget: (UIView*) target;
  - (void) droppableView: (JDDroppableView*) view leftTarget: (UIView*) target;
  - (BOOL) shouldAnimateDroppableViewBack: (JDDroppableView*) view wasDroppedOnTarget: (UIView*) target;
  - (void) droppableViewEndedDragging: (JDDroppableView*) view;
  @end
  
  @interface JDDroppableView : UIView
  {   
  UIView * mDropTarget;
  
  UIView * mOuterView;
  UIScrollView * mScrollView;
  
  BOOL mIsDragging;
  BOOL mIsOverTarget;
  CGPoint mOriginalPosition;
  }
  
  @property (nonatomic, assign) id&lt;JDDroppableViewDelegate&gt; delegate;
  
  - (id) initWithFrame:(CGRect)frame;
  - (id) initWithDropTarget:(UIView*)target;
  
  @end
  </code></pre>
  
  <p><strong>JDDroppableView.m</strong></p>
  
  <pre><code>#import "JDDroppableView.h"
  
  @interface JDDroppableView (hidden)
  - (void) beginDrag;
  - (void) dragAtPosition: (UITouch *) touch;
  - (void) endDrag;
  
  - (void) changeSuperView;
  - (BOOL) handleDroppedView;
  @end
  
  @implementation JDDroppableView
  
  @synthesize delegate = _delegate;
  
  - (id)init
  {
  return [self initWithFrame: [UIApplication sharedApplication].keyWindow.frame];
  }
  
  - (id)initWithFrame:(CGRect)frame
  {
  self = [super initWithFrame:frame];
  if (self) {
      mIsDragging = NO;
      mIsOverTarget = NO;
  }
  return self;
  }
  
  - (id) initWithDropTarget: (UIView *) target;
  {
  self = [self init];
  if (self != nil) {
      mDropTarget = target;
  }
  return self;
  }
  
  #pragma mark touch handling
  
  - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  {
  [self beginDrag];
  [self dragAtPosition: [touches anyObject]];
  }
  
  
  - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
  {
  [self dragAtPosition: [touches anyObject]];
  }
  
  
  - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  {   
  [self endDrag];
  }
  
  
  - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   {
  [self endDrag];
  }
  
  #pragma mark dragging logic
  
  - (void) beginDrag
  {
  mIsDragging = YES;
  
  if ([_delegate respondsToSelector: @selector(droppableViewBeganDragging:)]) {
      [_delegate droppableViewBeganDragging: self];
  };
  
  mOriginalPosition = self.center;
  
  [self changeSuperView];
  }
  
  
  - (void) dragAtPosition: (UITouch *) touch
  {
  [UIView beginAnimations: @"drag" context: nil];
  self.center = [touch locationInView: self.superview];
  [UIView commitAnimations];
  
  if ([_delegate respondsToSelector: @selector(droppableViewDidMove:)]) {
      [_delegate droppableViewDidMove:self];
  }
  
  if (mDropTarget) {
      CGRect intersect = CGRectIntersection(self.frame, mDropTarget.frame);
      if (intersect.size.width &gt; 10 || intersect.size.height &gt; 10)
      {
          if (!mIsOverTarget)
          {
              mIsOverTarget = YES;
  
              if ([_delegate respondsToSelector: @selector(droppableView:enteredTarget:)]) {
                  [_delegate droppableView: self enteredTarget: mDropTarget];
              }
          }
      }
      else if (mIsOverTarget)
      {
          mIsOverTarget = NO;
  
          if ([_delegate respondsToSelector: @selector(droppableView:leftTarget:)]) {
              [_delegate droppableView: self leftTarget: mDropTarget];
          }
      }
  }
  }
  
  
  - (void) endDrag
  {
  mIsOverTarget = NO;
  
  if([_delegate respondsToSelector: @selector(droppableViewEndedDragging:)]) {
      [_delegate droppableViewEndedDragging: self];
  }
  
  if (mDropTarget) {
      CGRect intersect = CGRectIntersection(self.frame, mDropTarget.frame);
      if (intersect.size.width &gt; 10 || intersect.size.height &gt; 10) {
  
          if([self handleDroppedView]) {
              mIsDragging = NO;
              return;
          }
      }
  }
  
  [self changeSuperView];
  mIsDragging = NO; // this needs to be after superview change
  
  [UIView beginAnimations: @"drag" context: nil];
  self.center = mOriginalPosition;
  [UIView commitAnimations];
  }
  
  - (BOOL) handleDroppedView
  {
  if (mDropTarget &amp;&amp; [_delegate respondsToSelector: @selector(shouldAnimateDroppableViewBack:wasDroppedOnTarget:)]) {
      return ![_delegate shouldAnimateDroppableViewBack: self wasDroppedOnTarget: mDropTarget];
  }
  
  return NO;
  }
  
  #pragma mark superview handling
  
  - (void)willMoveToSuperview:(id)newSuperview
  {
  if (!mIsDragging &amp;&amp; [newSuperview isKindOfClass: [UIScrollView class]]) {
      mScrollView = newSuperview;
      mOuterView = mScrollView.superview;
  }
  }
  
  - (void) changeSuperView
  {
  if (!mScrollView) {
      [self.superview bringSubviewToFront: self];
      return;
  }
  
  UIView * tmp = self.superview;
  
  [self removeFromSuperview];
  [mOuterView addSubview: self];
  
  mOuterView = tmp;
  
  // set new position
  
  CGPoint ctr = self.center;
  
  if (mOuterView == mScrollView) {
  
      ctr.x += mScrollView.frame.origin.x - mScrollView.contentOffset.x;
      ctr.y += mScrollView.frame.origin.y - mScrollView.contentOffset.y;
  } else {
  
      ctr.x -= mScrollView.frame.origin.x - mScrollView.contentOffset.x;
      ctr.y -= mScrollView.frame.origin.y - mScrollView.contentOffset.y;
  }
  
  self.center = ctr;
  }
  @end
  </code></pre>
  
  <p><strong>AppDelegate.h</strong></p>
  
  <pre><code>@interface AppDelegate : UIResponder &lt;UIApplicationDelegate&gt;
  {
  UIWindow* mWindow;
  TestViewController* mViewController;
  }
  
  @end
  </code></pre>
  
  <p><strong>AppDelegate.m</strong></p>
  
  <pre><code>#import "AppDelegate.h"
  
  @implementation AppDelegate
  
  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  {
  mWindow = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
  mWindow.backgroundColor = [UIColor whiteColor];
  
  TestViewController* viewController = [[TestViewController alloc] init];
  [mWindow addSubview: viewController.view];
  
  [mWindow makeKeyAndVisible];
  
  return YES;
  }
  
  - (void)dealloc
  {
  [mWindow release];
  [mViewController release];
  
  [super dealloc];
  }
  </code></pre>
  
  <p>@end</p>
  
  <p>Any help is much appreciated.</p>
  
  <p>Thank you.</p>