【发布时间】:2018-04-19 00:54:03
【问题描述】:
在 iOS 11 和 12 上长按 WKWebView 中的图像或链接会启动拖放会话(用户可以拖动图像或链接)。我怎样才能禁用它?
【问题讨论】:
标签: ios drag-and-drop ios11 wkwebview
在 iOS 11 和 12 上长按 WKWebView 中的图像或链接会启动拖放会话(用户可以拖动图像或链接)。我怎样才能禁用它?
【问题讨论】:
标签: ios drag-and-drop ios11 wkwebview
我确实找到了solution that involves method swizzling,但也可以在 WKWebView 中禁用拖放而无需任何调整。
注意:请参阅下面的 iOS 12.2+ 特别说明
WKContentView — WKWebView 的 WKScrollView 的私有子视图 — 具有 interactions 属性,就像 iOS 11+ 中的任何其他 UIView 一样。 interactions 属性包含 UIDragInteraction 和 UIDropInteraction。只需在UIDragInteraction 上将enabled 设置为false 即可。
我们不希望访问任何私有 API 并使代码尽可能可靠。
假设您的WKWebView 被称为webView:
if (@available(iOS 11.0, *)) {
// Step 1: Find the WKScrollView - it's a subclass of UIScrollView
UIView *webScrollView = nil;
for (UIView *subview in webView.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
webScrollView = subview;
break;
}
}
if (webScrollView) {
// Step 2: Find the WKContentView
UIView *contentView = nil;
// We don't want to trigger any private API usage warnings, so instead of checking
// for the subview's type, we simply look for the one that has two "interactions" (drag and drop)
for (UIView *subview in webScrollView.subviews) {
if ([subview.interactions count] > 1) {
contentView = subview;
break;
}
}
if (contentView) {
// Step 3: Find and disable the drag interaction
for (id<UIInteraction> interaction in contentView.interactions) {
if ([interaction isKindOfClass:[UIDragInteraction class]]) {
((UIDragInteraction *) interaction).enabled = NO;
break;
}
}
}
}
}
就是这样!
以上代码在 iOS 12.2 上仍然有效,但调用它很重要何时。在 iOS 12.1 及更低版本上,您可以在创建 WKWebView 后立即调用此代码。这已经不可能了。 WKContentView 的 interactions 数组在首次创建时为空。仅在将WKWebView 添加到附加到UIWindow 的视图层次结构后才会填充它 - 仅将其添加到尚不属于可见视图层次结构的超级视图是不够的。在视图控制器中,viewDidAppear 很可能是一个安全的调用它的地方。
UIDragInteraction的方法
setupDataInteractionDelegates)其实存在于WKContentView
-[WKContentView setupDataInteractionDelegates] 上设置了一个符号断点
bt 命令打印回溯这是输出:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 50.1
* frame #0: 0x00000001115b726c WebKit`-[WKContentView(WKInteraction) setupDataInteractionDelegates]
frame #1: 0x00000001115a8852 WebKit`-[WKContentView(WKInteraction) setupInteraction] + 1026
frame #2: 0x00000001115a5155 WebKit`-[WKContentView didMoveToWindow] + 79
很明显,UIDragInteraction 的创建和添加是由视图移动到(被添加到)窗口触发的。
【讨论】:
这很好用! 感谢@basha 的快速版本。
我也做了同样的事情,但使用了一些 compactMaps 来减少 if 语句的深度和警卫以摆脱强制展开。
private func disableDragAndDropInteraction() {
var webScrollView: UIView? = nil
var contentView: UIView? = nil
if #available(iOS 11.0, *) {
guard let noDragWebView = webView else { return }
webScrollView = noDragWebView.subviews.compactMap { $0 as? UIScrollView }.first
contentView = webScrollView?.subviews.first(where: { $0.interactions.count > 1 })
guard let dragInteraction = (contentView?.interactions.compactMap { $0 as? UIDragInteraction }.first) else { return }
contentView?.removeInteraction(dragInteraction)
}
}
【讨论】:
first(where: ...) 而不是compactMap(...).first 有什么特别的原因吗?我想知道,因为你用它来寻找contentView。
基于 Johannes FahrenKrug 的帖子,进行了一些更改。
private func disableDragAndDropInteraction() {
var webScrollView: UIView? = nil
var contentView: UIView? = nil
if #available(iOS 11.0, *) {
if (webView != nil) {
for subView in webView!.subviews {
if (subView is UIScrollView) {
webScrollView = subView
break
}
}
if (webScrollView != nil) {
for subView in webScrollView!.subviews {
if subView.interactions.count > 1 {
contentView = subView
break
}
}
if (contentView != nil) {
for interaction in contentView!.interactions {
if interaction is UIDragInteraction {
contentView!.removeInteraction(interaction)
}
}
}
}
} else {
// Fallback on earlier versions
}
}
}
【讨论】:
在img 和a 元素上使用CSS 属性webkit-touch-callout。还可以通过更改 HTML 或使用 -[WKWebView evaluateJavaScript(...)] 注入将其 draggable 属性设置为 false。
img, a {
-webkit-touch-callout: none;
}
<img draggable="false">
避免所有脆弱的子视图潜水和操纵。
(将my answer 概括为类似的Disable link drag on WKWebView。)
【讨论】: