处理UIWebview中的触摸事件

37

我创建了一个 UIWebView 的子类,并实现了 touchesBegantouchesMovedtouchesEnded 方法。

但是这个 Webview 子类没有处理 touch 事件。

是否有方法可以在 UIWebView 子类中处理触摸事件?


我不明白为什么您想要覆盖UIWebView处理的触摸事件。用户将希望在网站上缩放和缩小,以及触摸点击超链接等。您上面发布的代码没有在触摸方法中实现任何操作...我认为了解您想要实现的目标非常重要。在Web视图中覆盖触摸事件对我来说似乎是一个不好的想法。是否有其他方法可以实现您所需的应用程序行为? - Tim Bowen
3
我没想到开发者会有这样的评论。“在 web view 中覆盖触摸事件似乎不是个好主意。”注意一下“Stanza”和“Kindle”,它们是如何子类化/钩取 UIWebView 来处理事件的。 - Biranchi
只是在寻找更多信息,伙计,放松点。 - Tim Bowen
3
如果在子类化UIWebView时会让我产生怀疑。开发文档中说明它不能被子类化。虽然编译器、事件处理、运行循环等可能允许进行子类化,但我不相信可能出现的副作用。此外,在未来的版本中,你的代码可能完全停止工作。请问您想用子类化的UIWebView做什么? - mobibob
替换自定义UIWindow的方法在新的storyboarding方法中不起作用。也许苹果应该提供一些方法,至少返回触摸在UIWebView / WebView内的位置。这有多难(我知道这是著名的最后一句话)? - user975953
10个回答

47

无需创建子类,只需添加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];
在头文件中添加<UIGestureRecognizerDelegate>协议,并添加以下方法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

1
我需要在网页视图的顶部单击一次即可显示和隐藏导航控制器的导航栏,这种方法对我非常有效。 - brindy
2
我已经做了所有的事情,但在一个情况下,直到我也实现了UIGestureRecognizerDelegate- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch方法并返回YES,它才开始工作。 - g_fred

9

如果您只需要处理手势,同时保留其余UIWebView功能,则可以子类化UIWebView并使用以下策略:

在您的UIWebView子类的init方法中添加一个手势识别器,例如:

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

然后,将此方法添加到您的类中:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

将您指定的选择器添加到类中,并处理它,例如"handleSwipeGestureRightMethod",然后您就可以开始使用了...


使用手势识别器似乎是最干净的方式。 - Tudor

7
您可以在您的UIWebView上方放置一个UIView,并覆盖touchesDidBegin等方法,然后将它们发送到您的webview。例如:

用户触摸您的UIView,触发了

- (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;
}

您的 UIView 必须覆盖在 webview 上。


这是一个iPhone应用程序,它使用UIKit框架,我猜NSView仅用于开发Mac应用程序,并且在AppKit框架中可用。 - Biranchi

5

我不确定这是否是您想要的(这不是您要求的内容,但根据您的最终目标,它可能有效),但您可以在UIWebView中从JavaScript中解释触摸事件,并让JavaScript执行。

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

然后,您可以使用UIWebView的委托方法捕获它。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

如果 request.URL.host(或者其他类似的东西)等于“null”,则采取相应的措施(并返回 NO 而不是 YES)。您甚至可以通过执行以下操作将 JS 添加到每个页面中:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
  [webView stringByEvaluatingJavaScriptFromString:@"window.ontouchstart=function(/* ... */);"];
}

希望这能帮到你?


是的,虽然不是使用window.onmousedown!也许是在触摸开始时。 - Benjie
这种方法似乎不起作用。它似乎只适用于可点击的HTML元素,如图像和链接。当单击div、p、body、document标签时似乎没有效果 :( - mana

3

如何在UIWebView上处理手势,可以参考苹果开发者论坛中的这篇帖子

根据该帖子提供的信息,在大多数或所有情况下都不需要额外的视图,并且如前所述,重写UIWebView并非解决方法。

以下是该帖子中最重要的帖子:

这是一个已知问题。UIWebView有自己的UITapGestureRecognizers,并且它们位于UIWebView本身的私有子视图上。UIGestureRecognizer的优先级定义为与视图层次结构中更深的视图相关联的手势将排除superviews上的手势,因此Web视图的tap手势将始终胜过您的手势。

如果您的情况允许您允许您的敲击与Web视图的普通敲击同时发生,则最好的解决方案是实现UIGestureRecognizerDelegate方法gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer,并为其他敲击手势返回YES。这样,您将调用您的敲击处理程序,Web视图仍将调用其处理程序。

如果您需要是唯一处理轻拍的人,则必须对UITapGestureRecognizer进行子类化,以便您可以使用UIGestureRecognizerSubclass.h中的单向覆盖,并且当被问及web视图的轻拍手势识别器是否可以防止您的手势时,您可以从canBePreventedByGestureRecognizer:返回NO。

无论如何,我们已经了解到这个问题,并希望未来能更容易解决。


2

我刚刚发现,在用户点击UIWebView中的视图区域(而不是超链接或其他特定区域)时,UIWebView确实会检查是否响应- (void)webViewDidNotClick: (id)webBrowserView选择器。因此,你可以使用你的处理代码来实现该选择器 :)


由于某些原因,我无法使用这种方法检测到双击。 - ThE uSeFuL
这个应该放在哪里?我把它添加到了我的UIWebView代理中,但是没有起作用。 - zekel
1
不使用委托,您应该子类化UIWebView本身并重写此选择器,例如`
  • (void)webViewDidNotClick:(id)webBrowserView { [self.superview touchesEnded:nil withEvent:nil]; } `
- indexless
我猜这个现在已经被移除了。 - malhal

0

继Unfalkster所说的,您可以使用hitTest方法来实现您想要的效果,但您不必子类化UIWindow。只需将此代码放入您的Web视图子类中即可。您会收到编译时警告,但它确实有效:

- (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];
}

希望这能有所帮助!

1
很奇怪...由于某种原因[event allTouches]总是返回一个空集合。 - Mihai Damian
通常 hitTest:withEvent: 方法会返回一个 UIView*。这是有意忽略掉了吗? - ThomasW
如果你不从这个方法中返回一个UIView,它会崩溃。 - malhal

0

你的子类实现在调用touchesBegan、touchesMoved和touchesEnded时没有被调用吗?

这听起来像是你创建对象实例的问题。我认为需要更多的细节。

(摘自评论)

头文件

#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]; 
} 

@end

#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已加载"); }
  • (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"触摸开始"); }
  • (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"触摸结束"); }
  • (void)dealloc { [super dealloc]; } @end
- Biranchi
你在哪里调用 MyWebView *obj = [[MyWebView alloc] initWithFrame:CGMakeZero];? - Richard Stelling

0
如果您想检测自己的点击,但是禁用 UIWebView 的点击,则可以使用我的解决方案:
-(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';"];
}

0

我会尝试在UIWindow上重写-sendEvent:,看看是否可以拦截这些触摸事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接