Drag and drop in 3 lines

This post introduces the PuttyView class, which is built to act like a moveable and resizable window-like object in iOS.  It contains one primary view, called the contentView, and moves and resizes that view according to where the user drags their finger. (Source code link at the bottom.)

Drag and drop in 3 lines

Probably the coolest part of this code is the simplicity of getting drag-and-drop functionality in iOS.  Here’s the working implementation methods you need:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  touchStart = [[touches anyObject] locationInView:self];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  CGPoint point = [[touches anyObject] locationInView:self];
  self.center = CGPointMake(self.center.x + point.x - touchStart.x, self.center.y + point.y - touchStart.y);
}

Of course, I’m only counting “actual statement lines” in that “3 line” count, and you also need to declare touchStart as a CGPoint instance variable in the interface of your class. But still, the fact that this little work achieves drag-and-drop functionality is pretty cool.

Adding resizing

It’s just a little more work to add resizing. PuttyView classifies a finger drag as a resize if and only if it starts in the lower-right 15×15 block of the view. Here’s the main code for that:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  touchStart = [[touches anyObject] locationInView:self];
  isResizing = (self.bounds.size.width - touchStart.x < kResizeThumbSize &&
                self.bounds.size.height - touchStart.y < kResizeThumbSize);
  if (isResizing) {
    touchStart = CGPointMake(touchStart.x - self.bounds.size.width,
                             touchStart.y - self.bounds.size.height);
  }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  CGPoint touchPoint = [[touches anyObject] locationInView:self];
  if (isResizing) {
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y,
                            touchPoint.x - touchStart.x, touchPoint.y - touchStart.y);
  } else {
    self.center = CGPointMake(self.center.x + touchPoint.x - touchStart.x,
                              self.center.y + touchPoint.y - touchStart.y);
  }
}

Note that this code uses a new instance variable, a BOOL called isResizing.

PuttyView

The PuttyView class (source code in the demo zip file below) adds a little more to this by accepting a single view (the contentView) as a special subview that is resized in the same manner as PuttyView itself.  I chose to focus on a single special subview because I wanted to force users to explicitly react in their own setFrame: method, without having to worry about layout concerns or make any assumptions about how to resize and refit multiple subviews.

Sample xcode project

PuttyDemo.zip

9 Comments