5

I'm trying to create moving functionality to my imageView (maskPreview in the code below), so that users can move a picture, which is contained in maskPreview, around the screen. Here's my code for touch begin and touch moved:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count]==1) {
    UITouch *touch= [touches anyObject];
    originalOrigin = [touch locationInView:maskPreview];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count]==1) {
    UITouch *touch = [touches anyObject];
    CGPoint lastTouch = [touch previousLocationInView:self.view];
    CGFloat movedDistanceX = originalOrigin.x-lastTouch.x;
    CGFloat movedDistanceY = originalOrigin.y-lastTouch.y;
    [maskPreview setFrame:CGRectMake(maskPreview.frame.origin.x+movedDistanceX, maskPreview.frame.origin.y + movedDistanceY, maskPreview.frame.size.width, maskPreview.frame.size.height)];
}
}

but I'm getting some weird responses from the app. I haven't put restrictions on how far the imageview can move, i.e. to prevent it from going out of the screen, but even if it's a small move, my imageview goes wild and disappears.

Thanks alot in advance for all the help

Septronic
  • 1,156
  • 13
  • 32

2 Answers2

9

Implementing touchesBegan and so on is way overkill in this modern world. You're just confusing the heck out of yourself, and your code will quickly become impossible to understand or maintain. Use a UIPanGestureRecognizer; that's what it's for. Making a view draggable with a UIPanGestureRecognizer is trivial. Here's the action handler for a UIPanGestureRecognizer that makes the view draggable:

- (void) dragging: (UIPanGestureRecognizer*) p {
    UIView* vv = p.view;
    if (p.state == UIGestureRecognizerStateBegan ||
        p.state == UIGestureRecognizerStateChanged) {
        CGPoint delta = [p translationInView: vv.superview];
        CGPoint c = vv.center;
        c.x += delta.x; c.y += delta.y;
        vv.center = c;
        [p setTranslation: CGPointZero inView: vv.superview];
    }
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Awesome Matt, you are an absolute genius!! Would've rated your answer but I don't have enough reputation!! made my life so much easier!! – Septronic Feb 03 '12 at 13:47
  • Just FYI, that code came straight out of my book: http://shop.oreilly.com/product/0636920023562.do - Lots more where that came from... – matt Feb 03 '12 at 14:31
2

There are two problems with your code. First, this line is wrong:

CGPoint lastTouch = [touch previousLocationInView:self.view];

It should be this:

CGPoint lastTouch = [touch previousLocationInView:maskPreview];

Really, you shouldn't even be using previousLocationInView:. You should just be using locationInView: like this:

CGPoint lastTouch = [touch locationInView:maskPreview];

Second, you are getting the signs of movedDistanceX and movedDistanceY wrong. Change them to this:

CGFloat movedDistanceX = lastTouch.x - originalOrigin.x;
CGFloat movedDistanceY = lastTouch.y - originalOrigin.y;

Also, the documentation for touchesBegan:withEvent: says this:

If you override this method without calling super (a common use pattern), you must also override the other methods for handling touch events, if only as stub (empy) implementations.

So make sure you're also overriding touchesEnded:withEvent: and touchesCancelled:withEvent:.

Anyway, you can do this quite a bit more simply. One way is to make touchesBegan:withEvent: empty and do all the work in touchesMoved:withEvent: by using both previousLocationInView: and locationInView:, and updating maskPreview.center instead of maskPreview.frame. You won't even need the originalOrigin instance variable:

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

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if ([touches count]==1) {
        UITouch *touch = [touches anyObject];
        CGPoint p0 = [touch previousLocationInView:maskPreview];
        CGPoint p1 = [touch locationInView:maskPreview];
        CGPoint center = maskPreview.center;
        center.x += p1.x - p0.x;
        center.y += p1.y - p0.y;
        maskPreview.center = center;
    }
}

Another way to do this is by using a UIPanGestureRecognizer. I leave that as an exercise for the reader.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Hi Rob, Thanks for the answer, and realised the mistakes in my code!! I used Matt's tutorial for UIPanGestureRecognizer, and it worked a treat. But the bit about over-riding all touch events was a great help, I didn't know and didn't think we should do that. – Septronic Feb 03 '12 at 13:51