【问题标题】:Does UIGestureRecognizer work on a UIWebView?UIGestureRecognizer 是否适用于 UIWebView?
【发布时间】:2011-02-23 23:40:24
【问题描述】:

我试图让 UIGestureRecognizer 与 UIWebview 一起工作,它是 UIScrollView 的子视图。这听起来很奇怪,但是当我将 numberOfTouchesRequired 设置为 2 时,选择器会触发,但是当 numberOfTouchesRequired 设置为 1 时,选择器不会触发。

这是我的代码:

UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(select1:)];
    tap1.numberOfTouchesRequired = 2;
    tap1.numberOfTapsRequired = 1;
    tap1.delegate = self;
    [self.ans1WebView addGestureRecognizer:tap1];
    [tap1 release];

- (void) select1:(UILongPressGestureRecognizer *)sender {
    //Do Stuff
}

我已经通过使用 Apple 的 UIGestureRecognizer 示例并在他们的 nib 中插入 webview 来确认这一点。他们的点击代码在任何地方都有效,但在 web 视图区域内。

【问题讨论】:

    标签: iphone objective-c cocoa ipad uigesturerecognizer


    【解决方案1】:

    据我所见,UIWebView 不能很好地与其他人配合使用。对于手势识别器,您可以尝试从以下位置返回 YES:

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
    

    我必须向后兼容 3.0,所以我最终在 UIWebView 中使用 Javascript 进行所有手势处理。不是一个好的解决方案,但它满足我的需求。我能够捕捉一个元素上的长按,以及点击、滑动、捏合和旋转。当然,我只使用本地内容。

    编辑:

    您可以在PhoneGap 中查看向 UIWebView 的委托发送消息的示例。简而言之,您使用document.location = "myscheme:mycommand?myarguments",然后从这个委托回调中解析出命令和参数:

    -(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType {
      if ( [[[inRequest URL] absoluteString] hasPrefix:@"myscheme:"] ) {
        //.. parse arguments
        return NO;
      }
    }
    

    您可以在 PhoneGap 代码中看到他们设置了一个队列和计时器来发送消息。根据您的使用情况,您可能需要执行相同的操作。关于这个主题还有其他关于 SO 的问题。

    这是Event Handling 文档。就我而言,我从<body onload="myloader();">document 添加了事件侦听器

    function myloader() {
        document.addEventListener( 'touchcancel' , touch_cancel , false );
        document.addEventListener( 'touchstart' , touch_start , false );
        document.addEventListener( 'touchmove' , touch_move , false );
        document.addEventListener( 'touchend' , touch_end , false );
    };
    

    实际的事件处理很大程度上取决于您的需求。每个事件处理程序都会收到一个带有 touches 属性的TouchEvent,其中每个项目都是一个Touch。您可以在 touchstart 处理程序中记录开始时间和位置。如果触摸移动到很远或经过错误的时间,则不是长触摸。

    WebKit 可能会尝试处理长触摸来开始选择和复制。在您的事件处理程序中,您可以使用event.preventDefault(); 来停止默认行为。我还发现 -webkit-user-select:none css 属性对某些事情很方便。

    var touch = {};
    function touch_start( event /*TouchEvent*/ {
      event.preventDefault();
      touch = {};
      touch.startX = event.touches.item(0).pageX;
      touch.startY = event.touches.item(0).pageY;
      touch.startT = ( new Date() ).getTime();
    }
    

    这只是我使用 javascript 的第二个项目。您可以在其他地方找到更好的示例。

    如您所见,这不能快速解决您的问题。我对我得到的结果很满意。我正在使用的 html 除了一两个链接之外没有任何交互元素。

    【讨论】:

    • 谢谢,我对Javascript一无所知;您能否向我指出如何通过 Javascript 检测 UIWebView 中的点击并调用 Objective-C 方法的正确方向?对于非 iPad 定位,我不介意与 3.0 兼容。对于只关心 3.2+ 的任何其他人,我通过简单地在 UIWebView 顶部创建一个透明视图并检测其中的点击来解决我的问题。
    【解决方案2】:

    UIWebView 有自己的私有视图,它还附加了手势识别器。因此,优先规则使添加到 UIWebView 的任何手势识别器无法正常工作。

    一种选择是实现 UIGestureRecognizerDelegate 协议并实现方法gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer。从此方法返回 YES 用于其他点击手势。这样,您将调用您的点击处理程序,而 Web 视图仍将被调用。

    this on the Apple dev forums

    【讨论】:

    【解决方案3】:

    这是一个有点 hack-ish 的解决方案,但它确实有效。 UIWebView 有很多内部手势识别器,如果不使用私有 API,您通常无法访问它们。但是,当您实施gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: 时,您将免费获得它们。此时,您可以告诉所有内部 UITapGestureRecognizer 仅在您自己的 UITapGestureRecognizer 失败时触发。您只需执行一次,然后从您自己的手势识别器中删除委托。结果是您仍然可以平移和滚动您的 webView,但它不会再接收任何(单个)点击手势。

    - (void)viewDidLoad 
    {      
        [super viewDidLoad];
    
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
    
        // view is your UIViewController's view that contains your UIWebView as a subview
        [self.view addGestureRecognizer:tap];
    
        tap.delegate = self;
    }
    
    - (void)handleTapGesture:(UITapGestureRecognizer *)gestureRecognizer
    {
        NSLog(@"tap!");
    
        // this is called after gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: so we can safely remove the delegate here
        if (gestureRecognizer.delegate) {
            NSLog(@"Removing delegate...");
            gestureRecognizer.delegate = nil;
        }
    }
    
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
        if ([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
            [otherGestureRecognizer requireGestureRecognizerToFail:gestureRecognizer];
    
            NSLog(@"added failure requirement to: %@", otherGestureRecognizer);
        }
    
        return YES;
    }
    

    享受吧!

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题。这个解决方案对我有用:

      1) 确保将协议添加到您的界面:例如 UIGestureRecognizerDelegate:

      @interface ViewController : UIViewController <SlideViewProtocol, UIGestureRecognizerDelegate>
      

      2) 添加这行代码

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

      3) 设置手势

      UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(nextSlide)];
      swipeGesture.numberOfTouchesRequired = 1;
      swipeGesture.direction = (UISwipeGestureRecognizerDirectionLeft);
      swipeGesture.cancelsTouchesInView = YES; [swipeGesture setDelegate:self];
      [self.view addGestureRecognizer:swipeGesture];
      

      【讨论】:

      • 应编辑此答案以在第 2 步中返回正确的代码(即该 API 需要返回 BOOL)。
      • 这是最简单的解决方案,对我有用,但有两个注意事项。您需要返回 YES。而且你必须设置 swipeGesture.delegate = self;
      【解决方案5】:
      - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
          return YES;
      }
      

      【讨论】:

        【解决方案6】:

        另一种方法是在 UIWebView 上放置一个透明的 UIButton 并处理来自该按钮的点击。在某些情况下,这可能是一个很好的解决方案。

        UIWebView webView = [UIWebView new];
        //...
        
        UIButton *transparentButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [transparentButton addTarget:self action:@selector(buttonTapped) forControlEvents:(UIControlEventTouchUpInside)];
        transparentButton.frame = webView.frame;
        transparentButton.layer.backgroundColor = [[UIColor clearColor] CGColor];
        [self.view transparentButton];
        

        【讨论】:

        • 哈哈,这绝对是最好的解决方案 :) +1 黑客。
        • 如果不需要滚动 UIWebView,我会使用这种方法。但是如果你需要滚动,它就不起作用了,因为 UIButton 会捕获所有的用户输入。
        【解决方案7】:

        您也可能会发现我的回答 here 很有帮助。这是在 UIWebView 的 javascript 中处理捏合手势的工作示例。

        (如果您愿意,可以将事件传回 Objective-C。请参阅 my answer here。)

        【讨论】:

          猜你喜欢
          • 2019-10-16
          • 1970-01-01
          • 1970-01-01
          • 2013-09-07
          • 1970-01-01
          • 2017-11-24
          • 2013-06-23
          • 1970-01-01
          • 2015-07-13
          相关资源
          最近更新 更多