Handling touches inside UIWebview
Asked Answered
B

10

37

I have created a subclass of UIWebView , and have implemented the touchesBegan, touchesMoved and touchesEnded methods.

but the webview subclass is not handling the touch events.

Is there any method to handle the touch events inside the UIWebView subclass ???

Boswell answered 13/6, 2009 at 15:29 Comment(5)
I don't understand why you would ever want to override the touch events handled by UIWebView. Your user is going to want to zoom in and zoom out on the website, as well as touch to click hpyerlinks etc. The code you posted above doesn't have any implementation in the touch methods... I think it's important to know what you are trying to accomplish. Overriding touch events in a web view seems like a bad idea to me. Is there another way to accomplish the behavior you are looking for from your app?Elane
I don't expect such comments from a developer. "Overriding touch events in a web view seems like a bad idea to me" Watch out for "Stanza" and "Kindle" how they have subclassed/hooked the UIWebView for handling events.Boswell
Just looking for more information dude, relax.Elane
I would be suspect of any behavior when subclassing UIWebView. The dev doc says that it cannot be subclassed. I assume that the compiler, event handling, runloop, etc. will allow the subclass, but I would not trust the possible side effects. Also, your code could stop working all together in a future release. Can you explain what you want to do with a subclassed UIWebView?Urbanity
The approach of substituting a custom UIWindow will not work with the new storyboarding approach. Maybe Apple should have some methods that at least return where touches are inside the UIWebView/WebView. How hard could it be (famous last words, I know)?Jermainejerman
V
47

No subclassing needed, just add a UITapGestureRecognizer :

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapMethod)];
[tap setNumberOfTapsRequired:1]; // Set your own number here
[tap setDelegate:self]; // Add the <UIGestureRecognizerDelegate> protocol

[self.myWebView addGestureRecognizer:tap];

Add the <UIGestureRecognizerDelegate> protocol in the header file, and add this method:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
Voidable answered 18/9, 2012 at 14:25 Comment(2)
I needed to show and hide the navigation controller's navigation bar with a single tap on top of a web view and this method worked perfectly for me.Exercise
I did all that, however in one case, this was not working until I also implemented - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch of the UIGestureRecognizerDelegate, and returned YES.Ebullition
O
9

If all you need is to handle gestures, while leaving the rest of the UIWebView functionality intact, you can subclass UIWebView and use this strategy:

in the init method of your UIWebView subclass, add a gesture recognizer, e.g.:

UISwipeGestureRecognizer  * swipeRight = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeGestureRightMethod)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[self addGestureRecognizer:swipeRight];
swipeRight.delegate = self;

then, add this method to your class:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

Add and handle your designated selector to the class, in this case "handleSwipeGestureRightMethod" and you are good to go...

Osuna answered 20/3, 2012 at 0:57 Comment(1)
With gesture recognizers seems to be the cleanest way.Contextual
C
7

You could put an UIView over your UIWebView, and overide the touchesDidBegin etc, then send them to your webview. Ex:

User touches your UIView, which provokes a

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{  
    // Execute your code then send a touchesBegan to your webview like so:
[webView touchesBegan:touches withEvent:event];
return;
}

your UIView has to be over the webview.

Cheryle answered 22/11, 2009 at 0:26 Comment(1)
this is an iphone app and its using UIKit framework, i guess NSView is only for developing Mac Apps and is availabel in AppKit Framework.Boswell
L
5

I'm not sure if this is what you want (it's not what you asked for, but it might work depending on what your end game is), but you could instead interpret the touches in JavaScript from inside the UIWebView, and get javascript to do

document.location='http://null/'+xCoord+'/'+yCoord; // Null is arbitrary.

Then you can catch that using the UIWebView's delegate method

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

And if the request.URL.host (or whatever it is) isEqualToString:@"null" take the relevant action (and return NO instead of YES). You can even add the JS to each page by doing something like:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
  [webView stringByEvaluatingJavaScriptFromString:@"window.ontouchstart=function(/* ... */);"];
}

Hope this helps?

Listlessness answered 20/7, 2009 at 19:14 Comment(2)
Yes, though not with window.onmousedown! On touches began perhaps.Listlessness
This approach does not properly work. It seems only to work with clickable html-items as images and links. It seems to have no effect when clicking on div, p, body, document tags :(Mealie
T
3

Handling gestures on a UIWebView is discussed in this Apple Developer forum thread.

Using the info given there, there will be no need for an extra view in most or all cases, and as mentioned here before, overriding UIWebView is not the way to go.

Copypaste of the most important post in the thread:

This is a known issue. The UIWebView has its own UITapGestureRecognizers, and they're on a private subview of the UIWebView itself. UIGestureRecognizer precedence defines that gestures attached to views deeper in the view hierarchy will exclude ones on superviews, so the web view's tap gestures will always win over yours.

If it's okay in your case to allow your tap to happen along with the web view's normal tap your best solution would be to implement the UIGestureRecognizerDelegate method gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer and return YES for other tap gestures. This way you'll get your tap handler called, and the web view will still get its called.

If you need to be the only one handling the tap you'll have to subclass UITapGestureRecognizer so you can use the one-way overrides in UIGestureRecognizerSubclass.h, an you can then return NO from canBePreventedByGestureRecognizer: when asked if the web view's tap gesture recognizer can prevent yours.

In any case, we know about this and hope to make it easier in the future.

This answered 6/6, 2013 at 10:18 Comment(0)
T
2

I've just found that UIWebView does check whether it responds to the - (void)webViewDidNotClick: (id)webBrowserView selector, once one taps on the view area (not on hyperref, or any other area that should be handled specifically). So you may implement that selector with your handling code :)

Themistocles answered 5/10, 2010 at 19:41 Comment(4)
For some reason I can't detect double tap using this method.Dome
Where is this supposed to go? I added it to my UIWebView delegate but that didn't work.Rhoda
Not at delegate, you should subclass UIWebView itself and rewrite this selector such as ` -(void) webViewDidNotClick:(id)webBrowserView { [self.superview touchesEnded:nil withEvent:nil]; } `Themistocles
I guess this has been removed nowTaenia
T
0

Do you mean your sub-classed implementation is not called when touchesBegan, touchesMoved and touchesEnded are called?

It sounds like a problem with how you've created an instance of the object. More details are required I think.

(taken form comments)

Header File

#import <UIKit/UIKit.h> 

@interface MyWebView : UIWebView { } @end 

Implementation File

#import "MyWebView.h" 

@implementation MyWebView 

- (id)initWithFrame:(CGRect)frame { 
    if (self = [super initWithFrame:frame]) { } return self; 
} 

- (void)drawRect:(CGRect)rect { 
    NSLog(@"MyWebView is loaded"); 
} 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
   NSLog(@"touches began"); 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    NSLog(@"Touches ended");     
} 

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

@end
Thissa answered 13/6, 2009 at 15:45 Comment(2)
#import <UIKit/UIKit.h> @interface MyWebView : UIWebView { } @end #import "MyWebView.h" @implementation MyWebView - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { } return self; } - (void)drawRect:(CGRect)rect { NSLog(@"MyWebView is loaded"); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touches began"); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"Touches ended"); } - (void)dealloc { [super dealloc]; } @endBoswell
Where do you call MyWebView *obj = [[MyWebView alloc] initWithFrame:CGMakeZero];Thissa
I
0

I would try overriding -sendEvent: on UIWindow, to see if you can intercept those touch events.

Illsuited answered 14/11, 2009 at 8:57 Comment(0)
M
0

Following on from what Unfalkster said, you can use the hitTest method to achieve what you want, but you don't have to subclass UIWindow. Just put this in your web view subclass. You will get a compile time warning but it does work:

- (void)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (event.type == UIEventTypeTouches) {

        // get location info
        CGFloat x = point.x;
        CGFloat y = point.y;

        // get touches
        NSSet *touches = [event allTouches];

        // individual touches
        for (UITouch *touch in touches) {

            if (touch.phase == UITouchPhaseBegan) {
                // touches began

            } else if (touch.phase == UITouchPhaseMoved) {

            }

            // etc etc
        }
    }

    // call the super
    [super hitTest:point withEvent:event];
}

Hope that helps!

Milner answered 18/11, 2009 at 11:28 Comment(3)
That's weird... For some reason [event allTouches] always returns an empty setNonstriated
Normally the hitTest:withEvent: method returns a UIView*. Was that intentionally left out?Eous
it crashes if you don't return a UIView from this methodTaenia
T
0

If you want to detect your own taps but disable the UIWebView's taps then you can use my solution:

-(void)recursivelyDisableTapsOnView:(UIView*)v{
    for(UIView* view in v.subviews){
        for(UIGestureRecognizer* g in view.gestureRecognizers){
            if(g == self.ownTapRecognizer){
                continue;
            }
            if([g isKindOfClass:[UITapGestureRecognizer class]] ||
               [g isKindOfClass:[UILongPressGestureRecognizer class]] ||
               [g isKindOfClass:NSClassFromString(@"UITapAndAHalfRecognizer")]){
                g.enabled = NO;
            }
        }
        [self recursivelyDisableTapsOnView:view];
    }
}


- (void)webViewDidFinishLoad:(UIWebView *)webView{
    [self recursivelyDisableTapsOnView:webView];

    //disable selection
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
    // Disable callout
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
}
Taenia answered 12/7, 2013 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.