Swipe Gesture Recognition Inside UIWebView

2019-04-28 15:08发布

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).

3条回答
一夜七次
2楼-- · 2019-04-28 15:43

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.

查看更多
叛逆
3楼-- · 2019-04-28 15:46

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/

查看更多
霸刀☆藐视天下
4楼-- · 2019-04-28 15:51

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.

查看更多
登录 后发表回答