How to have a UISwipeGestureRecognizer AND UIPanGestureRecognizer work on the same view
How would you setup the gesture recognizers so that you could have a UISwipeGestureRecognizer and a UIPanGestureRecognizer work at the same time? Such that if you touch and move quickly (quick swipe) it detects the gesture as a swipe but if you touch then move (short delay between touch & move) it detects it as a pan?
I've tried various permutations of requireGestureRecognizerToFail and that didn't help exactly, it made it so that if the SwipeGesture was left then my pan gesture would work up, down and right but any movemen开发者_Go百科t left was detected by the swipe gesture.
You're going to want to set one of the two UIGestureRecognizer
's delegates to an object that makes sense (likely self
) then listen, and return YES
for this method:
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
This method is called when recognition of a gesture by either gestureRecognizer
or otherGestureRecognizer
would block the other gesture recognizer from recognizing its gesture. Note that returning YES
is guaranteed to allow simultaneous recognition; returning NO
, on the other hand, is not guaranteed to prevent simultaneous recognition because the other gesture recognizer's delegate may return YES
.
By default, when the user attempts to swipe, the gesture is interpreted as a pan. This is because a swiping gesture meets the necessary conditions to be interpreted as a pan (a continuous gesture) before it meets the necessary conditions to be interpreted as a swipe (a discrete gesture).
You need to indicate a relationship between two gesture recognizers by calling the requireGestureRecognizerToFail: method on the gesture recognizer that you want to delay
[self.panRecognizer requireGestureRecognizerToFail:self.swipeRecognizer];
Using a pan recognizer to detect swipping and panning:
- (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
}
}
}
Here is a full solution for detecting pan and swipe directions (utilizing 2cupsOfTech's swipeThreshold logic):
public enum PanSwipeDirection: Int {
case up, down, left, right, upSwipe, downSwipe, leftSwipe, rightSwipe
public var isSwipe: Bool { return [.upSwipe, .downSwipe, .leftSwipe, .rightSwipe].contains(self) }
public var isVertical: Bool { return [.up, .down, .upSwipe, .downSwipe].contains(self) }
public var isHorizontal: Bool { return !isVertical }
}
public extension UIPanGestureRecognizer {
var direction: PanSwipeDirection? {
let SwipeThreshold: CGFloat = 1000
let velocity = self.velocity(in: view)
let isVertical = abs(velocity.y) > abs(velocity.x)
switch (isVertical, velocity.x, velocity.y) {
case (true, _, let y) where y < 0: return y < -SwipeThreshold ? .upSwipe : .up
case (true, _, let y) where y > 0: return y > SwipeThreshold ? .downSwipe : .down
case (false, let x, _) where x > 0: return x > SwipeThreshold ? .rightSwipe : .right
case (false, let x, _) where x < 0: return x < -SwipeThreshold ? .leftSwipe : .left
default: return nil
}
}
}
Usage:
@IBAction func handlePanOrSwipe(recognizer: UIPanGestureRecognizer) {
if let direction = recognizer.direction {
if direction == .leftSwipe {
//swiped left
} else if direction == .up {
//panned up
} else if direction.isVertical && direction.isSwipe {
//swiped vertically
}
}
}
精彩评论