Long-pressing images or links in a WKWebView
on iOS 11 and 12 initiates a Drag & Drop session (the user can drag the image or the link). How can I disable that?
I did find a solution that involves method swizzling but it's also possible to disable drag and drop in a WKWebView without any swizzling.
Note: See special notes for iOS 12.2+ below
WKContentView
— a private subview of WKWebView
's WKScrollView
— has an interactions
property, just like any other UIView
in iOS 11+. That interactions
property contains both a UIDragInteraction
and a UIDropInteraction
. Simply setting enabled
to false
on the UIDragInteraction
does the trick.
We don't want to access any private APIs and make the code as solid as possible.
Assuming your WKWebView
is called 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;
}
}
}
}
}
That's it!
The above code still works on iOS 12.2, but it is important when to call it. On iOS 12.1 and below you could call this code right after creating the WKWebView
. That's not possible anymore. The WKContentView
's interactions
array is empty when it's first created. It is only populated after the WKWebView
is added to a view hierarchy that is attached to a UIWindow
- simply adding it to a superview that is not yet part of the visible view hierarchy is not enough. In a view controller viewDidAppear
would most likely be a safe place to call it from.
UIDragInteraction
setupDataInteractionDelegates
) actually exists on WKContentView
-[WKContentView setupDataInteractionDelegates]
bt
commandThis was the output:
* 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
So clearly the creation and addition of the UIDragInteraction
is triggered by the view moving to (being added to) a window.