问题
Problem:
Starting from iOS 13.4 WebKit intercepts gestures that previously were passed to the system and could be handled by UIGestureRecognizer
simultaneously
Demo project:
I created WKWebView
and added it to UIViewController
's view. I created also UIPinchGestureRecognizer
and added it to UIViewControler
's view as well.
class ViewController: UIViewController, UIGestureRecognizerDelegate, WKNavigationDelegate {
var webView: WKWebView?
override func viewDidLoad() {
super.viewDidLoad()
let pinch = UIPinchGestureRecognizer(target: self, action: #selector(self.pinchHandler))
pinch.delegate = self
self.view.addGestureRecognizer(pinch)
addWebView()
}
func addWebView() {
let webView = WKWebView()
webView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(webView)
NSLayoutConstraint.activate([
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.topAnchor.constraint(equalTo: view.topAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
self.webView = webView
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let localHtmlFile = Bundle.main.url(forResource: "index", withExtension: "html") {
let request = URLRequest(url: localHtmlFile)
webView?.load(request)
}
}
@objc func pinchHandler() {
debugPrint("PinchHandler called")
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive event: UIEvent) -> Bool {
if event.type == .touches {
return true
}
return false
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return true
}
}
I try to run sample html website added to main bundle:
<html>
<head>
<style>
.center {
width: 100%;
height: 100%;
border: 3px solid #73AD21;
text-align: center;
font-size: 100;
overflow: hidden;
}
</style>
<script>
function move(event) {
console.log(event);
event.preventDefault()
}
</script>
</head>
<body ontouchmove="move(event)" class="center">
<p id="content">
demo
</p>
</body
</html>
Website intercepts touchmove
event and I'm cancelling it in move(event)
function.
When I ran this code 10 times on iOS 13.0 Simulator, every time my handler worked. When I ran this code 10 times on iOS 13.4 Simulator, my pinch handler worked 4 times and didn't work 6 times.
回答1:
I ended up switching to use Javascript to detect the taps on the webview. A running playground showing how to do it:
import UIKit
import PlaygroundSupport
import WebKit
class TouchDetectionOnWKWebViewUsingJSDemoVC : UIViewController, WKScriptMessageHandler {
override func loadView() {
let config = WKWebViewConfiguration()
let webView = WKWebView(frame: .init(origin: .zero, size: .init(width: 100, height: 200)), configuration: config)
let jsEvent = "touchstart"
let handlerName = "handler"
let jsCode =
"""
window.addEventListener('\(jsEvent)', function(e) {
window.webkit.messageHandlers.\(handlerName).postMessage('\(jsEvent)')
})
"""
let jsScript = WKUserScript(source: jsCode, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
config.userContentController.addUserScript(jsScript)
config.userContentController.add(self, name: handlerName)
let demoURL = URL(string: "https://developer.apple.com/")!
webView.load(URLRequest(url: demoURL))
self.view = webView
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
}
PlaygroundPage.current.liveView = TouchDetectionOnWKWebViewUsingJSDemoVC()
In the example I show how to listen for touchstart
event, but it can be any js event, e.g.: touchend
, touchmove
, gesturestart
, gestureend
, and others. We can even listen to scroll events (but I think is better to be the webView.scrollView
delegate for that).
来源:https://stackoverflow.com/questions/61208881/cant-handle-touches-simultaneously-with-wkwebview-on-ios-13-4