Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone UIImageView pinch zoom

Tags:

iphone

I have been trying to implement pinch zoom/in-out for PhotoView (a UIImageView instance) using CGAffinTransformScale (planing to use rotation so can not count on frames for the zoom and will add subviews so UIScrollView would be more complicated, I think). Anyhow, the concept was easy enough to grasp and the code came together very quickly...Since then I have been trying to solve the same two (related?!) problems, using three different approaches, and have not be able to do so: 1- My code somehow loses track of the touch count in the middle of the zoom, from count = 2 to count = 1 and back on the iPhone, but not the simulator. 2- The touch points one and two keep jumping back and forth a few pixels on every move, causing the image to shrink and enlarge successively and rapidly, even though overall the effect is one of zooming out or zooming in as the user intended (both iPhone and simulator).

here's the code:

#import "PhotoView.h"


@implementation PhotoView;
@synthesize originalCenter, distance, zooming;
- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
    // Initialization code
        self.userInteractionEnabled = YES;
        self.multipleTouchEnabled = YES;
        zooming = NO;
    }
    return self;
}

float distanceBetweenTwoPoints(CGPoint point1, CGPoint point2)
{
 NSLog(@"point1 x: %5.2f point 2 x: %5.2f ---- point 1 y: %5.2f  point 2 y:    %5.2f",point1.x,point2.x,point1.y,point2.y);
    return (sqrt(pow(point1.x -point2.x,2) + pow(point1.y - point2.y,2)));
}
-(void) touchesBegan: (NSSet *) touches withEvent:(UIEvent *) event {

    if ([touches count] > 1) {

      NSLog(@"^^^^^^^^^^^^^^^Tocuhes began with double touch!");

      distance = distanceBetweenTwoPoints([[[touches allObjects] objectAtIndex:0] locationInView:self], 
        [[[touches allObjects] objectAtIndex:1] locationInView:self]);
      zooming = YES;
    }
    else {
       zooming = NO;
       origianlCenter = [[[touches allObjects] objectAtIndex:0] locationInView:self];
       NSLog(@">>>>>>>>>>>>Touches began with single touch");
    }
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    if (zooming) NSLog(@"!!!!!!!!!end zoom!!!!!!!");

    zooming = NO;
    if ([[touches anyObject] tapCount] == 2) {
        UITouch *thisTouch = [touches anyObject];
        CGPoint thisPoint = [thisTouch locationInView:self];
    }
}

- (void) touchesMoved: (NSSet *) touches withEvent:(UIEvent *) event {
    if ([touches count] > 1 && zooming) { // ignore if user added a second finger touch
        float distanceNew = distanceBetweenTwoPoints([[[touches allObjects] objectAtIndex:0]     locationInView:self], 
                 [[[touches allObjects] objectAtIndex:1] locationInView:self]);  
        if (distance <= 0.f) {    // should never be true - but it is sometimes!!!
           distance = distanceNew;
        }  
        float delta = 1.0f + ((distanceNew-distance)/distance);
        self.transform = CGAffineTransformScale(self.transform, delta, delta);
        distance = distanceNew;
    }
    else {
       if (zooming) {
          NSLog(@"*************shouldn't be here********* %d",[touches count]);
          return;
       }
       CGPoint thisPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self];
       self.transform = CGAffineTransformTranslate(self.transform, thisPoint.x-originalCenter.x, thisPoint.y-originalCenter.y);

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

The log sample:

^^^^^^^^^^^^^^^Tocuhes began with double touch!
point1 x: 87.33 point 2 x: 235.63 ---- point 1 y: 322.30  point 2 y: 117.09
point1 x: 90.76 point 2 x: 232.02 ---- point 1 y: 318.29  point 2 y: 123.51
point1 x: 86.22 point 2 x: 236.71 ---- point 1 y: 323.30  point 2 y: 117.42
point1 x: 89.51 point 2 x: 232.38 ---- point 1 y: 319.47  point 2 y: 123.47
point1 x: 84.97 point 2 x: 237.02 ---- point 1 y: 324.48  point 2 y: 116.56
*************shouldn't be here********* 1
point1 x: 88.49 point 2 x: 232.52 ---- point 1 y: 321.27  point 2 y: 122.91
*************shouldn't be here********* 1
point1 x: 83.95 point 2 x: 237.11 ---- point 1 y: 327.21  point 2 y: 116.96
!!!!!!!!!end zoom!!!!!!!

I am beginning to suspect that I am losing track of the touch points because of CGAffinTransformScale; however, I have not found anything online to suggest this to be an issue. Any clues (including 'read the documentation on xyz') would be appreciated!

Thanks in advance.

like image 574
cameron Avatar asked Nov 28 '22 20:11

cameron


2 Answers

An answer from the left-field perhaps, but an alternative might be to place the UIImageView inside a UIScrollView, define a viewForZoomingInScrollView: method on your scroll view delegate, and set the maximumZoomScale/minimumZoomScale properties and you'll have your pinch zooming without needing to implement the calculations to set the transform yourself? I've just done this on a recent project and it worked well.

like image 51
crafterm Avatar answered Dec 01 '22 09:12

crafterm


Generally speaking, whenever you implement a continuous UI behavior, you need to measure it against something that does not change.

So if your touches cause the View transformation to change, you should measure the touches against something that does not change - your parent view for instance. So, instead of calling :

[touch locationInView:self]

you should use

[touch locationInView:[self superview]]

I am not sure if this will fix your problem, but it will eliminate one possible cause of your troubles.

like image 22
Frank Krueger Avatar answered Dec 01 '22 10:12

Frank Krueger