Need help?

I'm available for remote short term contracting or consultancy work. Please check out my LinkedIn profile for more details on my experience.

Please feel free to use the form below to contact me.




Dragging View with Finger (iPhone)

If you're trying to programmatically figure out how to drag a view with your finger, then read on.

I was looking for a solution to this problem just the other day, and couldn't really find that much. What I needed was a view that you could move with your finger, and upon lifting the finger, it would snap into place at a set location. Seeing as I couldn't find anything like this, I decided to write my own custom view, which did just that.

The view I wrote extends UIImageView, but you can change that to UIView if you want. So how does this work you ask? Well, it basically takes a topLock, bottomLock, and whether it's moving on the x axis or not (if not, then it uses the y axis). The top lock is the x(or y) point where the view will lock into place, and same with the bottom lock. And how that works is that it'll check which one is closest, and lock it into that spot. So here's an example, if you drag the view up on the y axis all the way to the top and let go, it'll move it to the closest lock point (which will most likely be the top lock. This will also give that nice swing-back (gravity) effect that you see on the iPhone tableviews.

Below is the code for the 'FlipView' as I call it, and below that there is an example of how to use it.

FlipView.h:


#import ‹UIKit/UIKit.h›

@interface FlipView : UIImageView {
    
}

@property (nonatomic) int topLock;
@property (nonatomic) int bottomLock;
@property (nonatomic) CGPoint lastPoint;
@property (nonatomic) BOOL useXAxis;
@property (nonatomic, retain) UIView *parentView;

- (id)initWithFrame:(CGRect)frame andTopLock: (int) topLock andBottomLock: (int) bottomLock withXAxis: (BOOL) xAxis;

@end

FlipView.m:


#import "FlipView.h"

@implementation FlipView

@synthesize topLock = _topLock;
@synthesize bottomLock = _bottomLock;
@synthesize lastPoint = _lastPoint;
@synthesize useXAxis = _useXAxis;
@synthesize parentView = _parentView;

- (id)initWithFrame:(CGRect)frame andTopLock: (int) topLock
andBottomLock: (int) bottomLock withXAxis: (BOOL) useXAxis
{
   self = [super initWithFrame:frame];
   if (self) {
       // Initialization code
       self.userInteractionEnabled = YES;

       self.topLock = topLock;
       self.bottomLock = bottomLock;
       self.useXAxis = useXAxis;
   }

   return self;
}

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
   CGPoint pt = [[touches  anyObject] locationInView:self.parentView];

   //NSLog(@"dragging %f, %f", pt.x, pt.y);

   if(self.lastPoint.x != 0 && self.lastPoint.y != 0) {
       CGRect myFrame = self.frame;

       if (self.useXAxis) {
           if(pt.x < self.lastPoint.x) {
               myFrame.origin.x = (self.frame.origin.x) - (self.lastPoint.x - pt.x);
           } else {
               myFrame.origin.x = (self.frame.origin.x) + (pt.x - self.lastPoint.x);
           }
       } else {
           if(pt.y < self.lastPoint.y) {
                   myFrame.origin.y = (myFrame.origin.y) - (self.lastPoint.y - pt.y);
           } else {
                   myFrame.origin.y = (myFrame.origin.y) + (pt.y - self.lastPoint.y);
           }
       }

       self.frame = myFrame;
       self.lastPoint = pt;
   } else {
       self.lastPoint = pt;
   }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
   [UIView beginAnimations:nil context:nil];
   CGRect newFrame = self.frame;

   int lockLocation;
   if (self.useXAxis) {
       int topDiff = abs(self.lastPoint.x - self.topLock);
       int bottomDiff = abs(self.lastPoint.x - self.bottomLock);

       // checking which is closer
       if(topDiff < bottomDiff) {
           lockLocation = self.topLock;
       } else {
           lockLocation = self.bottomLock;
       }
   } else {
       int topDiff = abs(self.lastPoint.y - self.topLock);
       int bottomDiff = abs(self.lastPoint.y - self.bottomLock);

       // checking which is closer
       if(topDiff < bottomDiff) {
           lockLocation = self.topLock;
       } else {
           lockLocation = self.bottomLock;
       }

       //NSLog(@"top diff: %i | bottom diff: %i", topDiff, bottomDiff);
   }

   newFrame.origin.y = lockLocation;

   self.frame = newFrame;
   [UIView commitAnimations];

   // setting it back to zero so it doesn't get confused with the last point
   self.lastPoint = CGPointZero;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
   // Drawing code
}
*/

- (void)dealloc
{
   [super dealloc];
}

@end

You're probably wondering how this is used - well, here's an example below:


// creating the flip view
self.flipView = [[FlipView alloc] initWithFrame:CGRectMake(0, 300, 320, 471) andTopLock:150 andBottomLock:300 withXAxis:NO];
// we need to pass the parent view as well, which is used to determine the full size of the screen
self.flipView.parentView = self.view;
// here I am setting an image, but you won't need to do this if you don't want (if you're using a UIView)
self.flipView.image = [UIImage imageNamed:@"flip_panel.png"];

// adding the flip view to the view controllers main view
[self.view addSubview:self.flipView];

One thing I forgot to mention, is that you need to also pass the view of the parent view to the flipview.

You can also add any sub views to the flip view, as it works just like any other view would.

Feel free to modify this in any way you want, and hopefully this was helpful!


jon | August 14, 2011 | Comments (2)

Comments

Hi,

Your code is very usefull, but there are some "errors" (not really errors, your code works well) in your code, per example :

if(pt.x < self.lastPoint.x) {
myFrame.origin.x = (self.frame.origin.x) - (self.lastPoint.x - pt.x);
} else {
myFrame.origin.x = (self.frame.origin.x) + (pt.x - self.lastPoint.x);
}

This test is useless because both calculs are the same

myFrame.origin.x = (self.frame.origin.x) - (self.lastPoint.x - pt.x) = (self.frame.origin.x) - self.lastPoint.x + pt.x = (self.frame.origin.x) + (pt.x - self.lastPoint.x).

You also can put the line

self.lastPoint = pt;

outside the if block. There are many others examples.


The code works but it's not very optimized. But thanks a lot for your work.
Comment by Shawn - April 24, 2012 @ 9:30 am
I was seriosluy at DefCon 5 until I saw this post.
Comment by Maryellen - September 09, 2011 @ 6:39 pm

Name (required)
Email (will not be published) (required)
Website

captcha