5

I'm drawing a graph on a UIView, which is contained by a UIScrollView so that the user can scroll horizontally to look around the entire graph.

Now I want to zoom the graph when a user pinches in with two fingers, but instead of zooming in a view with the same rate for X and Y direction, I want to zoom only in the X direction by changing the X scale, without changing the Y scale.

I think I have to catch the pinch in/out gesture and redraw the graph, overriding the default zooming behavior.

But is there a way to do this?

I've been having a very difficult time to catch the pinch gesture on the UIScrollView, as it cancels the touches when it starts to scroll. I want the zooming to work even after the UIScrollView cancels the touches. :(

Thanks, Kura

Dharmesh Dhorajiya
  • 3,976
  • 9
  • 30
  • 39
Taka
  • 1,334
  • 17
  • 24

4 Answers4

12

Although you cannot delete the existing pinch gesture recognizer, you can disable it and then add your own:

// Disable existing recognizer
for (UIGestureRecognizer* recognizer in [_scrollView gestureRecognizers]) {
    if ([recognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
        [recognizer setEnabled:NO];
    }
}

// Add our own
UIPinchGestureRecognizer* pinchRecognizer = 
  [[UIPinchGestureRecognizer alloc] initWithTarget:self 
                                            action:@selector(pinch:)];
[_scrollView addGestureRecognizer:pinchRecognizer];
[pinchRecognizer release];

Then in

- (void) pinch:(UIPinchGestureRecognizer*)recognizer { .. }

use

[recognizer locationOfTouch:0 inView:..]
[recognizer locationOfTouch:1 inView:..]

to figure out if the user is pinching horizontally or vertically.

edsko
  • 1,628
  • 12
  • 19
  • That's a simple solution. :) Thanks edsko! – Taka Jul 18 '11 at 04:05
  • Even without disabling exiting gesture you can add new gesture. – Naveen Shan Jul 18 '11 at 11:10
  • 2
    Quick comment from the distant future: iOS 5, which came out three months after this answer was written, added `-[UIScrollView pinchGestureRecognizer]` so you don't have to do that linear search and hope that Apple didn't do anything too unorthodox. – Tommy Jul 07 '16 at 13:52
5

You should instead access the gestureRecognizers (defined in UIView), there are several of them being used by the scroll view,

figure out which one is the pinch recognizer and call removeGestureRecognizer: on the scroll view, then create your own and have it do the work, add it back with addGestureRecognizer:.

these are all public API, the recognizers and what order they are in are not (currently), so program defensively when accessing them

(this is a perfectly valid way to manipulate UIKit views, and Apple won't/shouldn't have issues with it - though they will not guarantee it works in any future release)

bshirley
  • 8,217
  • 1
  • 37
  • 43
  • +1 I was working on an answer for this with code examples but this is what needs to be done, the gesture recognizer fires before the touch events, usually not ever sending the touchesBegan and similar events. Loop through the gestureRecognizers and look for any that isKindOfClass: [UIPinchGestureRecognizer class] – Joe Jun 30 '11 at 18:56
  • bshirley & Joe, Thanks for your reply. I could find 3 gestureRecognizers on a UIScrollView instance, and one of them was UIScrollViewPinchGestureRecognizer. I tried to remove it and added my custom gesture recognizer, but it crashes as the UIScrollView always tries to send messages specifically to the UIScrollViewPinchGestureRecognizer object, which have been removed and released. I thought of an idea to call [UIGestureRecognizer removeTarget:action:] to remove the default pinch action and add a new one, but the action seems to be [UIScrollView handleGesture:], which is not documented. :( – Taka Jul 01 '11 at 04:25
  • add your recognizer, get a hold on that recognizer, and then tell that recognizer that it requires your to fail before it can succeed – bshirley Jul 01 '11 at 16:25
  • Thanks bshirley. I'd try that. ;) – Taka Jul 02 '11 at 05:22
1

You should be able to subclass UIScrollView and override the touchesBegan: method. Don't call [super touchesBegan:] but instead, adjust the zoom as you like:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //Anything you want. Probably you would want to store all the touches
    //or their values, so that you can compare them to the touches
    //in the touchesEnded: method, 
    //thus letting you know what the pinch amount was
}

If you like, you can judge whether it's a pinch or not, and if it's not, call the super method, and only handle it yourself for custom pinches.

Matthew Horst
  • 1,991
  • 15
  • 18
  • this likely won't work (anymore) because `UIScrollView` is now using multiple gesture recognizers, and in interactions between those and the `touch` methods, the methods usually lose - - - when trying to implement a "drag" out of a scroll view i had to do it with gesture recognizers – bshirley Jun 30 '11 at 19:03
  • That makes sense I guess. Although if you still wanted to do it this way, you ought to be able to just subclass UIView instead and get its touch events, then use those to manipulate a UIScrollView you put on top of the UIView? – Matthew Horst Jun 30 '11 at 20:40
  • Matt, thans for your reply. I've tried that, but the touches are canceled by UIScrollView once the scroll starts and cannot handle them anymore after that. It's the same if I subclass the UIScrollView, or subclass a UIView and put it on UIScrollView. – Taka Jul 01 '11 at 04:31
0

Edsko & bshirley answers are good, but they don't tell where to place the code.

First, I placed it in viewDidLoad method, but no Pinch Gesture Recognizer was found in the scrollview (maybe because my scrollview is an IBOutlet).

Then I tried in viewWillAppear or viewDidAppear and the UIPinchGestureRecognizer was here.

Martin
  • 11,881
  • 6
  • 64
  • 110