UIGestureRecognizer 可以在 UIWebView 上工作吗?

发布于 2024-09-03 10:57:04 字数 696 浏览 8 评论 0原文

我试图让 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
}

我通过使用 UIGestureRecognizer 的 Apple 示例并在其笔尖中插入一个 web 视图来确认这一点。他们的 Tap 代码可以在除 WebView 区域之外的任何地方工作。

I am attempting to get a UIGestureRecognizer working with a UIWebview which is a subview of a UIScrollView. This sounds odd but when I have the numberOfTouchesRequired set to 2 the selector fires, but when numberOfTouchesRequired is set to one the selector doesn't fire.

Here is my code:

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
}

I've confirmed this by using the Apple sample for UIGestureRecognizer and inserting a webview in their nib. Their tap code works everywhere but inside the area of the webview.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

旧伤还要旧人安 2024-09-10 10:57:04

据我所知,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 代码中看到它们设置了一个队列并定时器来发送消息。根据您的使用情况,您可能需要执行相同的操作。关于这个主题还有其他问题。

这是 事件处理文档。就我而言,我将事件侦听器添加到 中的 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 );
};

实际的事件处理很大程度上取决于您的需求。每个事件处理程序都会收到一个 TouchEvent ,其中每个项目都是一个 触摸。您可以在 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 除了一两个链接之外没有任何交互元素。

From what I have seen, UIWebView does not play well with others. For gesture recognizers, you could try returning YES from:

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

I had to be backwards compatible with 3.0 so I ended up doing all the gesture handling with Javascript inside the UIWebView. Not a good solution, but it worked for my needs. I was able to capture a long press on an element, as well as tap, swipe, pinch and rotate. Of course, I was using only local content.

Edit:

You can look in PhoneGap for an example of sending messages to the delegate of the UIWebView. In a nutshell, you use document.location = "myscheme:mycommand?myarguments" then parse out the command and arguments from this delegate callback:

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

You can see in the PhoneGap code that they set up a queue and timer to send the messages. Depending on your usage, you may need to do the same thing. There are other questions on SO on this topic.

Here is the Event Handling documentation. In my case I added event listeners to document from <body onload="myloader();">

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

The actual event handling depends a lot on your needs. Each event handler will receive a TouchEvent with a touches property where each item is a Touch. You can record the start time and location in your touchstart handler. If the touches move to far or the wrong amount of time passes it is not a long touch.

WebKit may try to handle a long touch to start a selection and copy. In your event handler you can use event.preventDefault(); to stop the default behavior. I also found the -webkit-user-select:none css property handy for some things.

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

This is only the second project I have used javascript with. You can find better examples elsewhere.

As you can see this is no quick answer to your problem. I am pretty happy with the results I got. The html I was working with had no interactive elements beyond a link or two.

糖果控 2024-09-10 10:57:04

UIWebView 有自己的私有视图,其中还附加了手势识别器。因此,优先规则会阻止添加到 UIWebView 的任何手势识别器正常工作。

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

请参阅Apple 开发者论坛上的内容

UIWebView has its own private views, which also has gesture recognizers attached. Hence, precedence rules keep any gesture recognizers added to a UIWebView from working properly.

One option is to implement the UIGestureRecognizerDelegate protocol and implement the method gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer. Return YES from this method for other tap gestures. This way you'll get your tap handler called, and the web view will still get its called.

See this on the Apple dev forums.

偏爱你一生 2024-09-10 10:57:04

这是一个有点黑客式的解决方案,但它确实有效。
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;
}

享受!

This is a somewhat hack-ish solution, but it works.
UIWebView has a lot of internal gesture recognizers that you normally cannot access without using private API. You get them all for free, though, when you implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. At that point, you can tell all the internal UITapGestureRecognizers to only fire when your own UITapGestureRecognizer has failed. You only do that once and then remove the delegate from your own gesture recognizer. The result is that you can still pan and scroll your webView, but it won't receive any (single) tap gestures anymore.

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

Enjoy!

‖放下 2024-09-10 10:57:04

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

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

I was having the same problem. This solution worked for me:

1) Make sure to add the protocol to your interface: UIGestureRecognizerDelegate for example:

@interface ViewController : UIViewController <SlideViewProtocol, UIGestureRecognizerDelegate>

2) Add this line of code

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

3) Setup gesture

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];
游魂 2024-09-10 10:57:04
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
    return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
    return YES;
}
南…巷孤猫 2024-09-10 10:57:04

另一种方法是在 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];

Another way is to put a transparent UIButton on top of UIWebView and handle taps from this button. In some cases it might be a good solution.

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];
烂人 2024-09-10 10:57:04

您还可以在这里< /a> 有帮助。这是在 UIWebView 的 javascript 中处理捏合手势的工作示例。

(如果您愿意,您可以将事件传回 Objective-C。请参阅我的回答。)

You may also find my answer here helpful. It's a working example of handling the pinch gesture in UIWebView's javascript.

(You could communicate the events back to the Objective-C if you wanted to. See my answer here.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文