Swipe Gesture Recognition Inside UIWebView
Asked Answered
D

3

6

I have read a bunch of questions on this but none of them seem to achieve what I am looking for... So lets say I have an arbitrary UIWebView inside of a UIViewController. The UIViewController has a SwipeGestureRecognizer that works fine. It even works within the UIWebView-- whenever there is no scrollbar. (Before I load a page or even if I load a page that can fit appropriately within the size of my UIWebView). However, if I load a webpage that requires horizontal scrolling left or right, then inside the UIWebView part of my view, I cannot get any swipe gestures to be recognized. Every click/drag/swipe just triggers the scroll action. Is there a way to differentiate between a "swipe" and just scrolling with your finger (not lifting it but rather dragging to scroll).

Dagny answered 8/8, 2012 at 16:48 Comment(0)
H
2

You will have to subclass UIWebView and override the gesture recogniser calls.

EDIT - Look at this post Handling touches inside UIWebview and this link http://mithin.in/2009/08/26/detecting-taps-and-events-on-uiwebview-the-right-way/

Husain answered 8/8, 2012 at 16:50 Comment(1)
Could you describe how to go about this? In the documentation it says "The UIWebView class should not be subclassed". developer.apple.com/library/ios/#documentation/uikit/reference/…Dagny
V
24

Yes, you can tell the UIWebView's UIScrollView that its UIPanGestureRecognizer should only fire when your own UISwipeGestureRecognizer has failed.

This is how you do it:

UISwipeGestureRecognizer *rightSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:)];
UISwipeGestureRecognizer *leftSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:)];
rightSwipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
leftSwipeGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:rightSwipeGesture];
[self.view addGestureRecognizer:leftSwipeGesture];

[_webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:rightSwipeGesture];
[_webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:leftSwipeGesture];

That should do the trick for you.

Vino answered 16/9, 2013 at 17:58 Comment(4)
Genius, saved my lot of time!Fairway
I haven't tested this but I think it will override the default scrolling of UIWebview. Isn't this right?Ratliff
Unfortunately this didn't work as expected. Everything seems to be as before. On horizontal scroll, if the web view has some horizontal list that requires scrolling then the swipe gestures won't be accepted there.Ratliff
@SyedDanishAli Are you talking about recognizing swipe gestures inside the web view using JavaScript AND natively using UISwipeGestureRecognizer on the same web view? I'm not surprised if that doesn't work: One of the swipe gesture recognizers has to "win".Vino
H
2

You will have to subclass UIWebView and override the gesture recogniser calls.

EDIT - Look at this post Handling touches inside UIWebview and this link http://mithin.in/2009/08/26/detecting-taps-and-events-on-uiwebview-the-right-way/

Husain answered 8/8, 2012 at 16:50 Comment(1)
Could you describe how to go about this? In the documentation it says "The UIWebView class should not be subclassed". developer.apple.com/library/ios/#documentation/uikit/reference/…Dagny
A
1

Johannes Fahrenkrug's answer succeeded in conditionally blocking the webView's built-in pan gestures. However, I found that this only worked if the webView's pan gestures were very slow ... if I panned the webView with any reasonable speed, the swipe gesture was triggered. I wanted only a fast swipe to trigger the swipe gesture, and medium or slow pans to use the default webView scrolling functionality.

The UISwipeGestureRecognizer has no properties for customizing the speed of a swipe, and the UIPanGestureRecognizer has a velocity property but no way to set a required velocity, so I set up a custom gesture recognizer based on this tutorial:

FastSwipeGestureRecognizer.h

#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

#define REQUIRED_TOUCHES 5
#define REQUIRED_STRAIGHTNESS 3
#define REQUIRED_TIME .1

typedef enum {
    DirectionUp = 0,
    DirectionRight,
    DirectionDown,
    DirectionLeft
} Direction;

@interface FastSwipeGestureRecognizer : UIGestureRecognizer {
    CGPoint firstTouchLocation;
    NSTimeInterval firstTouchTime;
    int touchesCount;

    Direction direction;
}

@property (nonatomic) CGPoint firstTouchLocation;
@property (nonatomic) NSTimeInterval firstTouchTime;
@property (nonatomic) int touchesCount;

@property (nonatomic) Direction direction;

@end

FastSwipeGestureRecognizer.m

#import "FastSwipeGestureRecognizer.h"

@implementation FastSwipeGestureRecognizer

@synthesize firstTouchLocation;
@synthesize firstTouchTime;
@synthesize touchesCount;

-(void)reset {
    [super reset];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    self.firstTouchLocation = [[touches anyObject] locationInView:self.view];
    self.firstTouchTime = [NSDate timeIntervalSinceReferenceDate];
    self.touchesCount = 1;
    self.state = UIGestureRecognizerStatePossible;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    self.touchesCount++;
    if (self.touchesCount > REQUIRED_TOUCHES) { // wait until we have a few touches before we evaluate the gesture
        CGPoint thisTouchLocation = [[touches anyObject] locationInView:self.view];
        float horizontalRatio = (ABS(thisTouchLocation.x - self.firstTouchLocation.x) / ABS(thisTouchLocation.y - self.firstTouchLocation.y));
        float verticalRatio = 1 / horizontalRatio;
        NSTimeInterval elapsedTime = [NSDate timeIntervalSinceReferenceDate] - self.firstTouchTime;
        NSLog(@"swipe? %f, %f, %f", verticalRatio, horizontalRatio, elapsedTime);

        // if we're moving straight enough and fast enough, complete the gesture
        if (((horizontalRatio > REQUIRED_STRAIGHTNESS)||(verticalRatio > REQUIRED_STRAIGHTNESS))&&(elapsedTime < REQUIRED_TIME)) {
            if (horizontalRatio > REQUIRED_STRAIGHTNESS) {
                self.direction = (thisTouchLocation.x > self.firstTouchLocation.x) ? DirectionRight : DirectionLeft ;
            } else if (verticalRatio > REQUIRED_STRAIGHTNESS) {
                self.direction = (thisTouchLocation.y > self.firstTouchLocation.y) ? DirectionDown : DirectionUp ;
            }
            self.state = UIGestureRecognizerStateRecognized;
        } else {
            self.state = UIGestureRecognizerStateFailed;
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    if (self.touchesCount < REQUIRED_TOUCHES) {
        self.state = UIGestureRecognizerStateFailed;
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    self.state = UIGestureRecognizerStateFailed;
}

@end

Set up your gestures

FastSwipeGestureRecognizer *swipeGesture = [[FastSwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:)];
[self.view addGestureRecognizer:swipeGesture];
[self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeGesture];

Then detect the direction of the received gesture

- (void)handleSwipeGesture:(FastSwipeGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded) {
        if (gesture.direction == DirectionRight) {
            // do something
        } else if (gesture.direction == DirectionLeft) {
            // do something
        } else if (gesture.direction == DirectionUp) {
            // do something
        } else if (gesture.direction == DirectionDown) {
            // do something
        }
    }
}

Note that this only requires one gesture to handle all four swipe directions, instead of one UISwipeGestureRecognizer per direction.

Amortizement answered 20/5, 2014 at 19:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.