CRAlpha / react-native-wkwebview

WKWebview Component for React Native
MIT License
642 stars 271 forks source link

Are tap (gesture) events unsupported? #153

Open shirakaba opened 6 years ago

shirakaba commented 6 years ago

In the iOS native WKWebView, one would approach gesture handling like so:

import WebKit
import UIKit
import JavaScriptCore
import Foundation

class ViewController: UIViewController {
    var webView: WKWebView?

    override func loadView() {
        super.loadView()
        webView = WKWebView(frame: self.view.frame, configuration: WKWebViewConfiguration())

    setUpTapGesture(target: webView!.scrollView, selector: #selector(onTap(gesture:)), delegate: self)
    }

    func setUpTapGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?){
        let gesture = UITapGestureRecognizer(target: self, action: selector)
        // gesture.cancelsTouchesInView = false
        gesture.delegate = delegate
        target.addGestureRecognizer(gesture)
    }
}

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

    @objc func onTap(gesture:UITapGestureRecognizer) -> Void {
        if(gesture.state == .ended){
            let touchPoint: CGPoint = gesture.location(in: view)
            let scrollPoint: CGPoint = gesture.location(in: webView!.scrollView)
            let zoomScale: CGFloat = webView!.scrollView.zoomScale
            // Now do something with this gesture information
        }
    }
}

How can we achieve full onTap functionality from the JavaScript side? I quickly tested whether <WKWebView> accepts an onClick={} prop, but it seems to not have any effect. If we wrap a <View> or <Touchable> component around it, I doubt we would get all the desired functionality (i.e. touch location, location in scrollview, and zoom level of scrollView); is this as-yet unimplemented? Has anyone out there made an implementation of it?

Are we also missing scrollViewDidScroll?

shirakaba commented 6 years ago

I did some work on this for my macOS fork (just change the NS to UI to try on iOS, pretty much:

// inside init()
NSClickGestureRecognizer * _gestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget: _webView action: @selector(handleGesture:)];
_gestureRecognizer.delegate = self;
[_webView addGestureRecognizer:_gestureRecognizer];
// Class method on RCTWKWebView, which now also implements NSGestureRecognizerDelegate
- (void)handleGesture:(NSGestureRecognizer *)gesture
{
  NSLog(@"handleGesture called!");
  if(gesture.state == NSGestureRecognizerStateEnded){
    NSPoint clickPoint = [gesture locationInView:_webView];
    NSLog(@"x: %f; y: %f", clickPoint.x, clickPoint.y);
  }
}

- (BOOL)gestureRecognizer:(NSGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(NSTouch *)touch NS_AVAILABLE_MAC(10_12_2) {
  return YES;
}

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

... However, I'm finding that the selector for handleGesture() fails to be found:

2018-05-31 23:38:15.927396+0100 MyApp[39471:630092] -[WKWebView handleGesture:]: unrecognized selector sent to instance 0x6000001a4980
2018-05-31 23:38:15.927971+0100 MyApp[39471:630092] [General] -[WKWebView handleGesture:]: unrecognized selector sent to instance 0x6000001a4980
2018-05-31 23:38:15.930627+0100 MyApp[39471:630092] [General] (
    0   CoreFoundation                      0x00007fff470cf32b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x00007fff6e749c76 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff47167e04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x00007fff47045870 ___forwarding___ + 1456
    4   CoreFoundation                      0x00007fff47045238 _CF_forwarding_prep_0 + 120
    5   AppKit                              0x00007fff44d7ca43 -[NSApplication(NSResponder) sendAction:to:from:] + 312
    6   AppKit                              0x00007fff4507cbf9 _NSGestureRecognizerSendActions + 206
    7   AppKit                              0x00007fff4507c527 -[NSGestureRecognizer _updateGestureWithEvent:] + 871
    8   AppKit                              0x00007fff4507ce3f -[NSGestureRecognizer _delayedUpdateGesture] + 60
    9   AppKit                              0x00007fff4507f66c ___NSGestureRecognizerUpdate_block_invoke + 51
    10  AppKit                              0x00007fff4507f5c8 _NSGestureRecognizerRemoveObjectsFromArrayAndApplyBlocks + 296
    11  AppKit                              0x00007fff4507947e _NSGestureRecognizerUpdate + 1497
    12  AppKit                              0x00007fff44f1dce7 -[NSWindow(NSGestureRecognizer_Routing) _sendEventToGestureRecognizers:requireAcceptsFirstMouse:] + 3090
    13  AppKit                              0x00007fff44f17c53 -[NSWindow(NSEventRouting) sendEvent:] + 468
    14  AppKit                              0x00007fff44d79236 -[NSApplication(NSEvent) sendEvent:] + 2462
    15  AppKit                              0x00007fff445d98b5 -[NSApplication run] + 812
    16  MyApp                     0x00000001000047a4 main + 548
    17  libdyld.dylib                       0x00007fff6f363015 start + 1

Upon adding NSLog(@"VERSION FOUR: I am %x", self); during the init process, I find that my WKWebView is in fact inited twice, and neither identifier matches the instance id to which the selector is being sent to. Despite two allocations, there is no dealloc either, so I really don't know what's happening.

Does WKWebView normally init itself twice..? Is this a bug with react-navigation or something that this module has always suffered?