Starting with iOS 3.2 Apple introduced a new easier way for developers to detect and use standard gestures in their applications.
They created UIGestureRecognizer and its sub-classes for dealing with this. You can now safely and easily detect the following:
- Tap (UITapGestureRecognizer)
- Pinch (UIPinchGestureRecognizer)
- Rotation (UIRotationGestureRecognizer)
- Swipe (UISwipeGestureRecognizer)
- Pan (UIPanGestureRecognizer)
- Long press (UILongPressGestureRecognizer)
I will not describe each gesture individually since there are some very good tutorials out there. I will just share some neat methods of combining these to detect more complex gestures.
Simultaneous Pan and Pinch Gesture Recognition
Image your user trying to view a picture in your awesome app. We want him to be able to zoom (pinch gesture) or drag around the picture (pan gesture) without lifting the fingers from the surface.
Here is some example code on how to setup your recognizers to achieve this behavior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | - (void)setupRecognizers { UIPanGestureRecognizer* panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognized:)]; panRecognizer.minimumNumberOfTouches = 1; panRecognizer.delegate = self; // Very important [someView addGestureRecognizer:panRecognizer]; UIPinchGestureRecognizer* pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGestureRecognized:)]; pinchRecognizer.delegate = self; // Very important [someView addGestureRecognizer:pinchRecognizer]; } #pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer <strong>shouldRecognizeSimultaneouslyWithGestureRecognizer</strong>:(UIGestureRecognizer *)otherGestureRecognizer { // If you have multiple gesture recognizers in this delegate, you can filter them by comparing the gestureRecognizer parameter to your saved objects return YES; // Also, very important. } |
As you can see, the trick is to add a delegate to both recognizers, and the return TRUE in the implemented delegate method. You can share the delegate object with other recognizers without any problems.
Improved Pan and Swipe Gesture Recognition
The standard UISwipeGestureRecognizer is good, but not great. For example, it has no way of controlling the minimum acceleration you must have when performing the gesture.
To go around this problem I used a pan gesture recognizer to detect the swipes. The good news is that you can even use the same recognizer to perform panning and swiping detection. Here is some example code on how to use it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | - (void)setupRecognizer { UIPanGestureRecognizer* panSwipeRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanSwipe:)]; // Here you can customize for example the minimum and maximum number of fingers required panSwipeRecognizer.minimumNumberOfTouches = 2; [targetView addGestureRecognizer:panSwipeRecognizer]; } #define SWIPE_UP_THRESHOLD -1000.0f #define SWIPE_DOWN_THRESHOLD 1000.0f #define SWIPE_LEFT_THRESHOLD -1000.0f #define SWIPE_RIGHT_THRESHOLD 1000.0f - (void)handlePanSwipe:(UIPanGestureRecognizer*)recognizer { // Get the translation in the view CGPoint t = [recognizer translationInView:recognizer.view]; [recognizer setTranslation:CGPointZero inView:recognizer.view]; // TODO: Here, you should translate your target view using this translation someView.center = CGPointMake(someView.center.x + t.x, someView.center.y + t.y); // But also, detect the swipe gesture if (recognizer.state == UIGestureRecognizerStateEnded) { CGPoint vel = [recognizer velocityInView:recognizer.view]; if (vel.x < SWIPE_LEFT_THRESHOLD) { // TODO: Detected a swipe to the left } else if (vel.x > SWIPE_RIGHT_THRESHOLD) { // TODO: Detected a swipe to the right } else if (vel.y < SWIPE_UP_THRESHOLD) { // TODO: Detected a swipe up } else if (vel.y > SWIPE_DOWN_THRESHOLD) { // TODO: Detected a swipe down } else { // TODO: // Here, the user lifted the finger/fingers but didn't swipe. // If you need you can implement a snapping behaviour, where based on the location of your targetView, // you focus back on the targetView or on some next view. // It's your call } } } |
Finally, you can even combine the 2 methods above to achieve an even more complete recognition solution.