Differencebetween Pan and Swipe in iOS

What is the difference between Pan and Swipe in iOS?

By definition, a swipe gesture is necessarily also a pan gesture -- both involve translational movement of touch points. The difference is in the recognizer semantics: a pan recognizer looks for the beginning of translational movement and continues to report movement in any direction over time, while a swipe recognizer makes an instantaneous decision as to whether the user's touches moved linearly in the required direction.

By default, no two recognizers will recognize the same gesture, so there's a conflict between pan and swipe. Most likely, your pan recognizer "wins" the conflict because its gesture is simpler / more general: A swipe is a pan but a pan may not be a swipe, so the pan recognizes first and excludes other recognizers.

You should be able to resolve this conflict using the delegate method gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:, or perhaps without delegation by making the pan recognizer depend on the swipe recognizer with requireGestureRecognizerToFail:.

With the conflict resolved, you should be able to simulate a one-finger swipe by quickly dragging the mouse. (Though as the mouse is more precise than your finger, it's a bit more finicky than doing the real thing on a device.) Two-finger pan/swipe can be done by holding the Option & Shift keys.

Pan (not swipe) between view controllers

I'm not sure UINavigationController is the correct container controller to provide what you need. You might find that UIPageViewController is a better choice, I think it will provide the natural swipe gesture you are looking for out of the box, although it's quite an opaque class with some quirks of its own.

To go the custom route (either with UINavigationController or the more flexible/customisable UIViewController custom presentation) you are on the right track with UIViewControllerAnimatedTransitioning and the other (numerous) associated protocols. In case you are in a hurry, that route will take you quite a long time to implement, so maybe a simple UIPageViewController implementation will have to do for the moment.

How to use pan gesture and a swipe gesture alternatively on the same view?

A swipe and a pan gesture can not be recognized at the same time because a swipe gesture is just a special type of pan that has to be faster, short lived, and in one direction.

What you should do is setup the pan gestures to require the swipe gesture to fail before they can start. You will need to set each of the UISwipeGestureRecognizer to be a requireGestureRecognizerToFail on the UIPanGestureRecognizer

override func viewDidLoad() {
super.viewDidLoad()

let swipeGestures = setupSwipeGestures()
setupPanGestures(swipeGestures: swipeGestures)
}

private func setupSwipeGestures() -> [UISwipeGestureRecognizer] {
let swipeUp = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
let swipeDown = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))

swipeUp.direction = .Up
swipeDown.direction = .Down

circleView.addGestureRecognizer(swipeUp)
circleView.addGestureRecognizer(swipeDown)

return [swipeUp, swipeDown]
}

private func setupPanGestures(swipeGestures: [UISwipeGestureRecognizer]) {
let panGesture = UIPanGestureRecognizer(target: self, action: Selector("handleThePan:"))
for swipeGeature in swipeGestures {
panGesture.requireGestureRecognizerToFail(swipeGesture)
}
circleView.addGestureRecognizer(panGesture)
}

Swift 3

override func viewDidLoad() {
super.viewDidLoad()

let swipeGestures = setupSwipeGestures()
setupPanGestures(swipeGestures: swipeGestures)
}

private func setupSwipeGestures() -> [UISwipeGestureRecognizer] {
let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(self.handleSwipes))
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.handleSwipes))

swipeUp.direction = .up
swipeDown.direction = .down

circleView.addGestureRecognizer(swipeUp)
circleView.addGestureRecognizer(swipeDown)

return [swipeUp, swipeDown]
}

private func setupPanGestures(swipeGestures: [UISwipeGestureRecognizer]) {
let panGesture = UIPanGestureRecognizer.init(target: self, action:#selector(self.handleThePanUp))
for swipeGesture in swipeGestures {
panGesture.require(toFail: swipeGesture)
}
circleView.addGestureRecognizer(panGesture)
}

swipe and pan gesture overlap

Check out the UIGestureRecognizerDelegate protocol here:

https://developer.apple.com/library/ios/documentation/uikit/reference/UIGestureRecognizerDelegate_Protocol/Reference/Reference.html

Specifically, the

gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

method might be useful. If you simply return YES from this method, both gestures can be recognized at the same time, so you can respond properly to both.

Can a pan gesture be used for moving UIWindows?

Yes, you can.

UIWindow is subclass of UIView and you can add PanGesture normally. To move window, change frame of UIApplication.sharedApplication.delegate.window, it will work fine.

Create a new project and replace AppDelegate.m file with my code below. You can move window.

#import "AppDelegate.h"

@interface AppDelegate ()

@property (nonatomic, strong) UIPanGestureRecognizer* panGesture;
@property (nonatomic, assign) CGPoint lastPoint;

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
[self.window addGestureRecognizer:_panGesture];
_needUpdate = YES;

return YES;
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)panGesture {
CGPoint point = [panGesture locationInView:self.window];
CGPoint center = self.window.center;

if (CGPointEqualToPoint(_lastPoint, CGPointZero)) {
_lastPoint = point;
}

center.x += point.x - _lastPoint.x;
center.y += point.y - _lastPoint.y;
self.window.frame = [UIScreen mainScreen].bounds;
self.window.center = center;

if (panGesture.state == UIGestureRecognizerStateEnded) {
_lastPoint = CGPointZero;
}
}


@end

How to have a UISwipeGestureRecognizer AND UIPanGestureRecognizer work on the same view

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.

How to tell whether a UIPageViewController page turn is due to a swipe/pan or a tap

The question is really how to determine which of several gesture recognizers is responsible for something happening.

The key thing to understand is that any gesture recognizer can have more than one target/action installed. The UIPageViewController sets its own target/actions internally, but that doesn't preclude others being added.

When setting up a sub-class of UIPageViewController after it has been loaded, or instantiated, add the following code (or Swift code equivalent):

for (UIGestureRecognizer *gr in self.gestureRecognizers) {
if ([gr class] == [UIPanGestureRecognizer class])
[gr addTarget:self action:@selector(panGestureOccurred)];
else if ([gr class] == [UITapGestureRecognizer class])
[gr addTarget:self action:@selector(tapGestureOccurred)];
}

Then, add the two target/action methods to the sub-class:

- (void)tapGestureOccurred
{
// Set a flag here to rely on later after the page turn
}

- (void)panGestureOccurred
{
// Reset a flag here to rely on later after the page turn
}

This obviously generalizes for any other types of gestures, but currently only the pan and tap gestures appear to be supported by a UIPageViewController.

panGestureOccurred will be called multiple times while the user is touching the screen and moving a finger/stylus. The tapGestureOccurred is only called once.

But only one of the two methods will be called, depending on which gesture recognizer the page view controller in its infinite wisdom decides wins. This all seems to work much more robustly than the too-low level touchesBegan and touchesMoved idea posited in the original question.



Related Topics



Leave a reply



Submit