Disable entire UIMenuController edit menu in WKWebView
Requirement
I have a WKWebView
and would like to remove the system menu items (Copy, Define, Share...) from the Edit Menu and present my own.
I am targeting iOS 8 and 9. I am currently testing with the Xcode 7.0.1 simulator (iOS 9) and my iPhone 6 running iOS 9.0.2.
Standard Method Does Not Work
I know the standard way of achieving this is by subclassing WKWebView
and implementing
-canPerformAction:withSender:
. However, I have found that with WKWebView
-canPerformAction:withSender:
is not being called for the copy:
or define:
actions. This appears to be a known bug (WKWebView and UIMenuController).
Example app: https://github.com/dwieringa/WKWebViewCustomEditMenuBug
@implementation MyWKWebView
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(@"ACTION: %@", NSStringFromSelector(action));
if (action == @selector(delete:))
{
// adding Delete as test (works)
return YES;
}
// trying to remove everything else (does NOT work for Copy, Define, Share...)
return NO;
}
- (void)delete:(id)sender
{
NSLog(@"Delete menu item selected");
}
@end
Output: (note no copy:
or define:
action)
2015-10-20 12:28:32.864 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: cut:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: select:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: selectAll:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: paste:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: delete:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _promptForReplace:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _transliterateChinese:
2015-10-20 12:28:32.867 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _showTextStyleOptions:
2015-10-20 12:28:32.907 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _addShortcut:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeak:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeakLanguageSelection:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilityPauseSpeaking:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionRightToLeft:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionLeftToRight:
Planned Workaround
My desire now is to completely hide the edit menu and replace it with a custom menu using QBPopupMenu.
My problem is that I have not been able to find a way to hide or disable the standard Edit menu. I have found some suggestions to hide it with [UIMenuController sharedMenuController].menuVisible = NO;
on UIMenuControllerWillShowMenuNotification
, but I have not been able to get this to work. It has no affect with WillShowMenu
. I can hide it in DidShowMenu
but by that point it is too late and I get a menu flash.
I have also tried to locate it outside the visible area using [[UIMenuController sharedMenuController] setTargetRect:CGRectMake(0, 0, 1, 1) inView:self.extraView];
, but again doing so with WillShowMenu
has no affect and with DidShowMenu
it is too late.
Experiments available here: https://github.com/dwieringa/WKWebViewEditMenuHidingTest
What am I missing? Is there another way to disable or hide the standard editting menu for WKWebView?
ios wkwebview uimenucontroller
add a comment |
Requirement
I have a WKWebView
and would like to remove the system menu items (Copy, Define, Share...) from the Edit Menu and present my own.
I am targeting iOS 8 and 9. I am currently testing with the Xcode 7.0.1 simulator (iOS 9) and my iPhone 6 running iOS 9.0.2.
Standard Method Does Not Work
I know the standard way of achieving this is by subclassing WKWebView
and implementing
-canPerformAction:withSender:
. However, I have found that with WKWebView
-canPerformAction:withSender:
is not being called for the copy:
or define:
actions. This appears to be a known bug (WKWebView and UIMenuController).
Example app: https://github.com/dwieringa/WKWebViewCustomEditMenuBug
@implementation MyWKWebView
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(@"ACTION: %@", NSStringFromSelector(action));
if (action == @selector(delete:))
{
// adding Delete as test (works)
return YES;
}
// trying to remove everything else (does NOT work for Copy, Define, Share...)
return NO;
}
- (void)delete:(id)sender
{
NSLog(@"Delete menu item selected");
}
@end
Output: (note no copy:
or define:
action)
2015-10-20 12:28:32.864 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: cut:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: select:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: selectAll:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: paste:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: delete:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _promptForReplace:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _transliterateChinese:
2015-10-20 12:28:32.867 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _showTextStyleOptions:
2015-10-20 12:28:32.907 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _addShortcut:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeak:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeakLanguageSelection:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilityPauseSpeaking:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionRightToLeft:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionLeftToRight:
Planned Workaround
My desire now is to completely hide the edit menu and replace it with a custom menu using QBPopupMenu.
My problem is that I have not been able to find a way to hide or disable the standard Edit menu. I have found some suggestions to hide it with [UIMenuController sharedMenuController].menuVisible = NO;
on UIMenuControllerWillShowMenuNotification
, but I have not been able to get this to work. It has no affect with WillShowMenu
. I can hide it in DidShowMenu
but by that point it is too late and I get a menu flash.
I have also tried to locate it outside the visible area using [[UIMenuController sharedMenuController] setTargetRect:CGRectMake(0, 0, 1, 1) inView:self.extraView];
, but again doing so with WillShowMenu
has no affect and with DidShowMenu
it is too late.
Experiments available here: https://github.com/dwieringa/WKWebViewEditMenuHidingTest
What am I missing? Is there another way to disable or hide the standard editting menu for WKWebView?
ios wkwebview uimenucontroller
Out of curiosity have you filed a bug report with Apple for this?
– Ryan
Nov 19 '15 at 0:48
@Ryan, no not yet. Thanks for your answer below. I just tried it and I'm still seeing the menu when I long press on text under iOS 9 on my iPhone 6. Have you tried it with WKWebView on iOS? I verified with Web Inspector that the new CSS setting is applied to body.
– davew
Nov 19 '15 at 1:58
@davew any success with this?
– Jed Grant
Nov 29 '15 at 1:48
@JedGrant no, I've been living with the standard Editing menu for now. On Nov 19, I briefly tried Ryan's suggestion, but didn't get it to work and haven't had time to dig deeper.
– davew
Dec 1 '15 at 21:01
add a comment |
Requirement
I have a WKWebView
and would like to remove the system menu items (Copy, Define, Share...) from the Edit Menu and present my own.
I am targeting iOS 8 and 9. I am currently testing with the Xcode 7.0.1 simulator (iOS 9) and my iPhone 6 running iOS 9.0.2.
Standard Method Does Not Work
I know the standard way of achieving this is by subclassing WKWebView
and implementing
-canPerformAction:withSender:
. However, I have found that with WKWebView
-canPerformAction:withSender:
is not being called for the copy:
or define:
actions. This appears to be a known bug (WKWebView and UIMenuController).
Example app: https://github.com/dwieringa/WKWebViewCustomEditMenuBug
@implementation MyWKWebView
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(@"ACTION: %@", NSStringFromSelector(action));
if (action == @selector(delete:))
{
// adding Delete as test (works)
return YES;
}
// trying to remove everything else (does NOT work for Copy, Define, Share...)
return NO;
}
- (void)delete:(id)sender
{
NSLog(@"Delete menu item selected");
}
@end
Output: (note no copy:
or define:
action)
2015-10-20 12:28:32.864 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: cut:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: select:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: selectAll:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: paste:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: delete:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _promptForReplace:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _transliterateChinese:
2015-10-20 12:28:32.867 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _showTextStyleOptions:
2015-10-20 12:28:32.907 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _addShortcut:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeak:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeakLanguageSelection:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilityPauseSpeaking:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionRightToLeft:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionLeftToRight:
Planned Workaround
My desire now is to completely hide the edit menu and replace it with a custom menu using QBPopupMenu.
My problem is that I have not been able to find a way to hide or disable the standard Edit menu. I have found some suggestions to hide it with [UIMenuController sharedMenuController].menuVisible = NO;
on UIMenuControllerWillShowMenuNotification
, but I have not been able to get this to work. It has no affect with WillShowMenu
. I can hide it in DidShowMenu
but by that point it is too late and I get a menu flash.
I have also tried to locate it outside the visible area using [[UIMenuController sharedMenuController] setTargetRect:CGRectMake(0, 0, 1, 1) inView:self.extraView];
, but again doing so with WillShowMenu
has no affect and with DidShowMenu
it is too late.
Experiments available here: https://github.com/dwieringa/WKWebViewEditMenuHidingTest
What am I missing? Is there another way to disable or hide the standard editting menu for WKWebView?
ios wkwebview uimenucontroller
Requirement
I have a WKWebView
and would like to remove the system menu items (Copy, Define, Share...) from the Edit Menu and present my own.
I am targeting iOS 8 and 9. I am currently testing with the Xcode 7.0.1 simulator (iOS 9) and my iPhone 6 running iOS 9.0.2.
Standard Method Does Not Work
I know the standard way of achieving this is by subclassing WKWebView
and implementing
-canPerformAction:withSender:
. However, I have found that with WKWebView
-canPerformAction:withSender:
is not being called for the copy:
or define:
actions. This appears to be a known bug (WKWebView and UIMenuController).
Example app: https://github.com/dwieringa/WKWebViewCustomEditMenuBug
@implementation MyWKWebView
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(@"ACTION: %@", NSStringFromSelector(action));
if (action == @selector(delete:))
{
// adding Delete as test (works)
return YES;
}
// trying to remove everything else (does NOT work for Copy, Define, Share...)
return NO;
}
- (void)delete:(id)sender
{
NSLog(@"Delete menu item selected");
}
@end
Output: (note no copy:
or define:
action)
2015-10-20 12:28:32.864 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: cut:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: select:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: selectAll:
2015-10-20 12:28:32.865 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: paste:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: delete:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _promptForReplace:
2015-10-20 12:28:32.866 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _transliterateChinese:
2015-10-20 12:28:32.867 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _showTextStyleOptions:
2015-10-20 12:28:32.907 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _addShortcut:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeak:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilitySpeakLanguageSelection:
2015-10-20 12:28:32.908 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: _accessibilityPauseSpeaking:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionRightToLeft:
2015-10-20 12:28:32.909 WKWebViewCustomEditMenuBug[45804:21121480] ACTION: makeTextWritingDirectionLeftToRight:
Planned Workaround
My desire now is to completely hide the edit menu and replace it with a custom menu using QBPopupMenu.
My problem is that I have not been able to find a way to hide or disable the standard Edit menu. I have found some suggestions to hide it with [UIMenuController sharedMenuController].menuVisible = NO;
on UIMenuControllerWillShowMenuNotification
, but I have not been able to get this to work. It has no affect with WillShowMenu
. I can hide it in DidShowMenu
but by that point it is too late and I get a menu flash.
I have also tried to locate it outside the visible area using [[UIMenuController sharedMenuController] setTargetRect:CGRectMake(0, 0, 1, 1) inView:self.extraView];
, but again doing so with WillShowMenu
has no affect and with DidShowMenu
it is too late.
Experiments available here: https://github.com/dwieringa/WKWebViewEditMenuHidingTest
What am I missing? Is there another way to disable or hide the standard editting menu for WKWebView?
ios wkwebview uimenucontroller
ios wkwebview uimenucontroller
edited May 23 '17 at 11:47
Community♦
11
11
asked Oct 20 '15 at 17:37
davewdavew
763825
763825
Out of curiosity have you filed a bug report with Apple for this?
– Ryan
Nov 19 '15 at 0:48
@Ryan, no not yet. Thanks for your answer below. I just tried it and I'm still seeing the menu when I long press on text under iOS 9 on my iPhone 6. Have you tried it with WKWebView on iOS? I verified with Web Inspector that the new CSS setting is applied to body.
– davew
Nov 19 '15 at 1:58
@davew any success with this?
– Jed Grant
Nov 29 '15 at 1:48
@JedGrant no, I've been living with the standard Editing menu for now. On Nov 19, I briefly tried Ryan's suggestion, but didn't get it to work and haven't had time to dig deeper.
– davew
Dec 1 '15 at 21:01
add a comment |
Out of curiosity have you filed a bug report with Apple for this?
– Ryan
Nov 19 '15 at 0:48
@Ryan, no not yet. Thanks for your answer below. I just tried it and I'm still seeing the menu when I long press on text under iOS 9 on my iPhone 6. Have you tried it with WKWebView on iOS? I verified with Web Inspector that the new CSS setting is applied to body.
– davew
Nov 19 '15 at 1:58
@davew any success with this?
– Jed Grant
Nov 29 '15 at 1:48
@JedGrant no, I've been living with the standard Editing menu for now. On Nov 19, I briefly tried Ryan's suggestion, but didn't get it to work and haven't had time to dig deeper.
– davew
Dec 1 '15 at 21:01
Out of curiosity have you filed a bug report with Apple for this?
– Ryan
Nov 19 '15 at 0:48
Out of curiosity have you filed a bug report with Apple for this?
– Ryan
Nov 19 '15 at 0:48
@Ryan, no not yet. Thanks for your answer below. I just tried it and I'm still seeing the menu when I long press on text under iOS 9 on my iPhone 6. Have you tried it with WKWebView on iOS? I verified with Web Inspector that the new CSS setting is applied to body.
– davew
Nov 19 '15 at 1:58
@Ryan, no not yet. Thanks for your answer below. I just tried it and I'm still seeing the menu when I long press on text under iOS 9 on my iPhone 6. Have you tried it with WKWebView on iOS? I verified with Web Inspector that the new CSS setting is applied to body.
– davew
Nov 19 '15 at 1:58
@davew any success with this?
– Jed Grant
Nov 29 '15 at 1:48
@davew any success with this?
– Jed Grant
Nov 29 '15 at 1:48
@JedGrant no, I've been living with the standard Editing menu for now. On Nov 19, I briefly tried Ryan's suggestion, but didn't get it to work and haven't had time to dig deeper.
– davew
Dec 1 '15 at 21:01
@JedGrant no, I've been living with the standard Editing menu for now. On Nov 19, I briefly tried Ryan's suggestion, but didn't get it to work and haven't had time to dig deeper.
– davew
Dec 1 '15 at 21:01
add a comment |
10 Answers
10
active
oldest
votes
Try making your view controller become first responder and stop it from resigning first responder
- (BOOL)canResignFirstResponder {
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
https://github.com/dwieringa/WKWebViewEditMenuHidingTest/pull/1
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
add a comment |
Based on your workaround, I found out that:
-(void)menuWillShow:(NSNotification *)notification
{
NSLog(@"MENU WILL SHOW");
dispatch_async(dispatch_get_main_queue(), ^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
Will prevent the menu from flashing 90% of the times.. Still not good enough, but it's another workaround before we find a decent solution.
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
1
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
add a comment |
Hey guys after spending a hours on it, i found dirty solution with %100 success rate.
Logic is; detect when UIMenuController did shown and update it.
In your ViewController(containing WKWebView) add UIMenuControllerDidShowMenu observer in viewDidLoad() like this;
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(uiMenuViewControllerDidShowMenu),
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
Don't forget to remove observer in deinit.
deinit {
NotificationCenter.default.removeObserver(
self,
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
And in your selector, update UIMenuController like this:
func uiMenuViewControllerDidShowMenu() {
if longPress {
let menuController = UIMenuController.shared
menuController.setMenuVisible(false, animated: false)
menuController.update() //You can only call this and it will still work as expected but i also call setMenuVisible just to make sure.
}
}
In your ViewController who ever calls the UIMenuController, this method will get called. I am developing browser app so i have also searchBar and user may want to paste text to there. Because of that i detect longPress in my webview and check if UIMenuController is summoned by WKWebView.
This solution will behave like in gif. You can see menu for a second but you can't tap it. You can try to tap it before it fades away but you won't succeed. Please try and tell me your results.
I hope it helps someone.
Cheers.
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
add a comment |
This bug is actually caused by the actions being added in the WKContentView, which is a private class. You could add a UIView extension to work around it like this:
import UIKit
extension UIView {
open override class func initialize() {
guard NSStringFromClass(self) == "WKContentView" else { return }
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
fileprivate class func swizzleMethod(_ selector: Selector, withSelector: Selector) {
let originalSelector = class_getInstanceMethod(self, selector)
let swizzledSelector = class_getInstanceMethod(self, withSelector)
method_exchangeImplementations(originalSelector, swizzledSelector)
}
@objc fileprivate func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
1
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
add a comment |
I Fixed it after some observation.
In -canPerformAction:withSender:
I am returning NO
for _share
and _define
options as I don't need them in my project. It works as expected on selection of word for first time, but shows up the options from second time.
Simple fix: Add [self becomeFirstResponder];
in tapGuesture
or Touch delegate methods
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
SEL defineSEL = NSSelectorFromString(@"_define:");
if(action == defineSEL){
return NO;
}
SEL shareSEL = NSSelectorFromString(@"_share:");
if(action == shareSEL){
return NO;
}
return YES;
}
// Tap gesture delegate method
- (void)singleTap:(UITapGestureRecognizer *)sender {
lastTouchPoint = [sender locationInView:self.webView];
[self becomeFirstResponder]; //added this line to fix the issue//
}
add a comment |
Here's my final solution, adapted from the solutions posted here. The key is to listen for the UIMenuControllerWillShowMenu
notification and then Dispatch.main.async
to hide the menu. This seems to do the trick to avoid the flashing menu.
My example uses a UITextField
, but it should be easily adapted to a WKWebView
.
class NoMenuTextField: UITextField {
override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
deregisterForMenuNotifications()
} else {
registerForMenuNotifications()
}
}
func registerForMenuNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.UIMenuControllerWillShowMenu,
object: nil,
queue: OperationQueue.main)
{ _ in
DispatchQueue.main.async {
UIMenuController.shared.setMenuVisible(false, animated: false)
UIMenuController.shared.update()
}
}
}
func deregisterForMenuNotifications() {
NotificationCenter.default.removeObserver(self,
name: Notification.Name.UIMenuControllerWillShowMenu,
object: nil)
}
}
add a comment |
I tried the solution from Stephan Heilner but it didn't compile in Swift 4.
This is my implementation to disable the menuController in a WKWebView that works with Swift 4.
In my WKWebView subclass, I added these property and function :
var wkContentView: UIView? {
return self.subviewWithClassName("WKContentView")
}
private func swizzleResponderChainAction() {
wkContentView?.swizzlePerformAction()
}
Then, I added an extension in the same file, but out of the WKWebView subclass :
// MARK: - Extension used for the swizzling part linked to wkContentView (see above)
extension UIView {
/// Find a subview corresponding to the className parameter, recursively.
func subviewWithClassName(_ className: String) -> UIView? {
if NSStringFromClass(type(of: self)) == className {
return self
} else {
for subview in subviews {
return subview.subviewWithClassName(className)
}
}
return nil
}
func swizzlePerformAction() {
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
private func swizzleMethod(_ currentSelector: Selector, withSelector newSelector: Selector) {
if let currentMethod = self.instanceMethod(for: currentSelector),
let newMethod = self.instanceMethod(for:newSelector) {
let newImplementation = method_getImplementation(newMethod)
method_setImplementation(currentMethod, newImplementation)
} else {
print("Could not find originalSelector")
}
}
private func instanceMethod(for selector: Selector) -> Method? {
let classType = type(of: self)
return class_getInstanceMethod(classType, selector)
}
@objc private func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
And finally, I called the swizzleResponderChainAction()
function from the initializer (you can either override the designated initializer, or create a convenience one):
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
swizzleResponderChainAction()
}
Now, the WKWebView does not crash anymore when using a UIMenuController.
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
add a comment |
One way that I've used is to simply disable the menu using CSS. The CSS property is called -webkit-touch-callout: none;
. You can apply it to the top level element and disable it for the whole page or any child element and disable it with more precision. Hope that helps.
add a comment |
pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// Add:
// Disable LongPress and Selection, no more UIMenucontroller
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil]; }
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
add a comment |
In iOS 11, I have found a simple solution by an extension of WKWebView. I have not checked to see if this will work in earlier versions of iOS. The following is a simple example with one menu item.
import UIKit
import WebKit
extension WKWebView {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(highlightHandler):
return true
default:
return false
}
}
func createEditMenu() { // Should be called once
let highlight = UIMenuItem(title: "Highlight", action: #selector(highlightHandler))
menuItems.append(highlight)
UIMenuController.shared.menuItems = [highlight]
}
@objc func highlightHandler(sender: UIMenuItem) {
print("highlight clicked")
}
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f33243230%2fdisable-entire-uimenucontroller-edit-menu-in-wkwebview%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
10 Answers
10
active
oldest
votes
10 Answers
10
active
oldest
votes
active
oldest
votes
active
oldest
votes
Try making your view controller become first responder and stop it from resigning first responder
- (BOOL)canResignFirstResponder {
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
https://github.com/dwieringa/WKWebViewEditMenuHidingTest/pull/1
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
add a comment |
Try making your view controller become first responder and stop it from resigning first responder
- (BOOL)canResignFirstResponder {
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
https://github.com/dwieringa/WKWebViewEditMenuHidingTest/pull/1
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
add a comment |
Try making your view controller become first responder and stop it from resigning first responder
- (BOOL)canResignFirstResponder {
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
https://github.com/dwieringa/WKWebViewEditMenuHidingTest/pull/1
Try making your view controller become first responder and stop it from resigning first responder
- (BOOL)canResignFirstResponder {
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
https://github.com/dwieringa/WKWebViewEditMenuHidingTest/pull/1
answered Feb 7 '16 at 1:16
ranunezranunez
31829
31829
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
add a comment |
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This appears to have solved it for me.
– Jon Brooks
Feb 26 '16 at 2:00
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
This is correct answer. I have used the same in swift 3, xcode 8.2 and worked fine. Here is the swift code. override var canBecomeFirstResponder: Bool { return true } override var canResignFirstResponder: Bool { return false }
– Vijay
Jan 13 '17 at 3:17
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
Does not work for WkWebView
– Vitaliy Litvinov
Apr 26 '17 at 14:20
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
This is problematic if you have text input in your WKWebView.
– André Morujão
Dec 19 '17 at 16:54
add a comment |
Based on your workaround, I found out that:
-(void)menuWillShow:(NSNotification *)notification
{
NSLog(@"MENU WILL SHOW");
dispatch_async(dispatch_get_main_queue(), ^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
Will prevent the menu from flashing 90% of the times.. Still not good enough, but it's another workaround before we find a decent solution.
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
1
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
add a comment |
Based on your workaround, I found out that:
-(void)menuWillShow:(NSNotification *)notification
{
NSLog(@"MENU WILL SHOW");
dispatch_async(dispatch_get_main_queue(), ^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
Will prevent the menu from flashing 90% of the times.. Still not good enough, but it's another workaround before we find a decent solution.
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
1
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
add a comment |
Based on your workaround, I found out that:
-(void)menuWillShow:(NSNotification *)notification
{
NSLog(@"MENU WILL SHOW");
dispatch_async(dispatch_get_main_queue(), ^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
Will prevent the menu from flashing 90% of the times.. Still not good enough, but it's another workaround before we find a decent solution.
Based on your workaround, I found out that:
-(void)menuWillShow:(NSNotification *)notification
{
NSLog(@"MENU WILL SHOW");
dispatch_async(dispatch_get_main_queue(), ^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
Will prevent the menu from flashing 90% of the times.. Still not good enough, but it's another workaround before we find a decent solution.
answered Dec 11 '15 at 16:57
Paulo CesarPaulo Cesar
1,42011829
1,42011829
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
1
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
add a comment |
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
1
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
I'm OK with this. +1
– Ahmed Khalaf
Oct 24 '17 at 8:39
1
1
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
Not sure if this changed since you posted, but for me this is working (as in, the menu is never actually displayed) 100% of the time after trying at least 30 times :)
– André Morujão
Jan 9 '18 at 16:08
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
I don't even work on that project anymore, but that's good to know :)
– Paulo Cesar
Jan 10 '18 at 15:15
add a comment |
Hey guys after spending a hours on it, i found dirty solution with %100 success rate.
Logic is; detect when UIMenuController did shown and update it.
In your ViewController(containing WKWebView) add UIMenuControllerDidShowMenu observer in viewDidLoad() like this;
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(uiMenuViewControllerDidShowMenu),
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
Don't forget to remove observer in deinit.
deinit {
NotificationCenter.default.removeObserver(
self,
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
And in your selector, update UIMenuController like this:
func uiMenuViewControllerDidShowMenu() {
if longPress {
let menuController = UIMenuController.shared
menuController.setMenuVisible(false, animated: false)
menuController.update() //You can only call this and it will still work as expected but i also call setMenuVisible just to make sure.
}
}
In your ViewController who ever calls the UIMenuController, this method will get called. I am developing browser app so i have also searchBar and user may want to paste text to there. Because of that i detect longPress in my webview and check if UIMenuController is summoned by WKWebView.
This solution will behave like in gif. You can see menu for a second but you can't tap it. You can try to tap it before it fades away but you won't succeed. Please try and tell me your results.
I hope it helps someone.
Cheers.
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
add a comment |
Hey guys after spending a hours on it, i found dirty solution with %100 success rate.
Logic is; detect when UIMenuController did shown and update it.
In your ViewController(containing WKWebView) add UIMenuControllerDidShowMenu observer in viewDidLoad() like this;
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(uiMenuViewControllerDidShowMenu),
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
Don't forget to remove observer in deinit.
deinit {
NotificationCenter.default.removeObserver(
self,
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
And in your selector, update UIMenuController like this:
func uiMenuViewControllerDidShowMenu() {
if longPress {
let menuController = UIMenuController.shared
menuController.setMenuVisible(false, animated: false)
menuController.update() //You can only call this and it will still work as expected but i also call setMenuVisible just to make sure.
}
}
In your ViewController who ever calls the UIMenuController, this method will get called. I am developing browser app so i have also searchBar and user may want to paste text to there. Because of that i detect longPress in my webview and check if UIMenuController is summoned by WKWebView.
This solution will behave like in gif. You can see menu for a second but you can't tap it. You can try to tap it before it fades away but you won't succeed. Please try and tell me your results.
I hope it helps someone.
Cheers.
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
add a comment |
Hey guys after spending a hours on it, i found dirty solution with %100 success rate.
Logic is; detect when UIMenuController did shown and update it.
In your ViewController(containing WKWebView) add UIMenuControllerDidShowMenu observer in viewDidLoad() like this;
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(uiMenuViewControllerDidShowMenu),
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
Don't forget to remove observer in deinit.
deinit {
NotificationCenter.default.removeObserver(
self,
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
And in your selector, update UIMenuController like this:
func uiMenuViewControllerDidShowMenu() {
if longPress {
let menuController = UIMenuController.shared
menuController.setMenuVisible(false, animated: false)
menuController.update() //You can only call this and it will still work as expected but i also call setMenuVisible just to make sure.
}
}
In your ViewController who ever calls the UIMenuController, this method will get called. I am developing browser app so i have also searchBar and user may want to paste text to there. Because of that i detect longPress in my webview and check if UIMenuController is summoned by WKWebView.
This solution will behave like in gif. You can see menu for a second but you can't tap it. You can try to tap it before it fades away but you won't succeed. Please try and tell me your results.
I hope it helps someone.
Cheers.
Hey guys after spending a hours on it, i found dirty solution with %100 success rate.
Logic is; detect when UIMenuController did shown and update it.
In your ViewController(containing WKWebView) add UIMenuControllerDidShowMenu observer in viewDidLoad() like this;
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(uiMenuViewControllerDidShowMenu),
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
Don't forget to remove observer in deinit.
deinit {
NotificationCenter.default.removeObserver(
self,
name: NSNotification.Name.UIMenuControllerDidShowMenu,
object: nil)
}
And in your selector, update UIMenuController like this:
func uiMenuViewControllerDidShowMenu() {
if longPress {
let menuController = UIMenuController.shared
menuController.setMenuVisible(false, animated: false)
menuController.update() //You can only call this and it will still work as expected but i also call setMenuVisible just to make sure.
}
}
In your ViewController who ever calls the UIMenuController, this method will get called. I am developing browser app so i have also searchBar and user may want to paste text to there. Because of that i detect longPress in my webview and check if UIMenuController is summoned by WKWebView.
This solution will behave like in gif. You can see menu for a second but you can't tap it. You can try to tap it before it fades away but you won't succeed. Please try and tell me your results.
I hope it helps someone.
Cheers.
answered Feb 28 '17 at 14:01
Yasin NazlıcanYasin Nazlıcan
304414
304414
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
add a comment |
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
hi how can we do this in objective c?
– coder
Nov 23 '17 at 9:08
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
Paulo Cesar's solution avoids showing the menu for that fraction of a second: stackoverflow.com/a/34228687/374516
– André Morujão
Jan 9 '18 at 15:24
add a comment |
This bug is actually caused by the actions being added in the WKContentView, which is a private class. You could add a UIView extension to work around it like this:
import UIKit
extension UIView {
open override class func initialize() {
guard NSStringFromClass(self) == "WKContentView" else { return }
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
fileprivate class func swizzleMethod(_ selector: Selector, withSelector: Selector) {
let originalSelector = class_getInstanceMethod(self, selector)
let swizzledSelector = class_getInstanceMethod(self, withSelector)
method_exchangeImplementations(originalSelector, swizzledSelector)
}
@objc fileprivate func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
1
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
add a comment |
This bug is actually caused by the actions being added in the WKContentView, which is a private class. You could add a UIView extension to work around it like this:
import UIKit
extension UIView {
open override class func initialize() {
guard NSStringFromClass(self) == "WKContentView" else { return }
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
fileprivate class func swizzleMethod(_ selector: Selector, withSelector: Selector) {
let originalSelector = class_getInstanceMethod(self, selector)
let swizzledSelector = class_getInstanceMethod(self, withSelector)
method_exchangeImplementations(originalSelector, swizzledSelector)
}
@objc fileprivate func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
1
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
add a comment |
This bug is actually caused by the actions being added in the WKContentView, which is a private class. You could add a UIView extension to work around it like this:
import UIKit
extension UIView {
open override class func initialize() {
guard NSStringFromClass(self) == "WKContentView" else { return }
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
fileprivate class func swizzleMethod(_ selector: Selector, withSelector: Selector) {
let originalSelector = class_getInstanceMethod(self, selector)
let swizzledSelector = class_getInstanceMethod(self, withSelector)
method_exchangeImplementations(originalSelector, swizzledSelector)
}
@objc fileprivate func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
This bug is actually caused by the actions being added in the WKContentView, which is a private class. You could add a UIView extension to work around it like this:
import UIKit
extension UIView {
open override class func initialize() {
guard NSStringFromClass(self) == "WKContentView" else { return }
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
fileprivate class func swizzleMethod(_ selector: Selector, withSelector: Selector) {
let originalSelector = class_getInstanceMethod(self, selector)
let swizzledSelector = class_getInstanceMethod(self, withSelector)
method_exchangeImplementations(originalSelector, swizzledSelector)
}
@objc fileprivate func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
answered Mar 23 '17 at 19:35
Stephan HeilnerStephan Heilner
50239
50239
1
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
add a comment |
1
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
1
1
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
This solution does not compile in Swift 4. I get the following error message: "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
– Frédéric Adda
Mar 6 '18 at 13:22
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@FrédéricAdda did you find any solution to this?. regarding Method initialize().
– Srini
Jan 8 at 11:58
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
@Srini, yes, I posted it below: stackoverflow.com/a/54095482/1552730
– Frédéric Adda
Jan 8 at 16:04
add a comment |
I Fixed it after some observation.
In -canPerformAction:withSender:
I am returning NO
for _share
and _define
options as I don't need them in my project. It works as expected on selection of word for first time, but shows up the options from second time.
Simple fix: Add [self becomeFirstResponder];
in tapGuesture
or Touch delegate methods
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
SEL defineSEL = NSSelectorFromString(@"_define:");
if(action == defineSEL){
return NO;
}
SEL shareSEL = NSSelectorFromString(@"_share:");
if(action == shareSEL){
return NO;
}
return YES;
}
// Tap gesture delegate method
- (void)singleTap:(UITapGestureRecognizer *)sender {
lastTouchPoint = [sender locationInView:self.webView];
[self becomeFirstResponder]; //added this line to fix the issue//
}
add a comment |
I Fixed it after some observation.
In -canPerformAction:withSender:
I am returning NO
for _share
and _define
options as I don't need them in my project. It works as expected on selection of word for first time, but shows up the options from second time.
Simple fix: Add [self becomeFirstResponder];
in tapGuesture
or Touch delegate methods
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
SEL defineSEL = NSSelectorFromString(@"_define:");
if(action == defineSEL){
return NO;
}
SEL shareSEL = NSSelectorFromString(@"_share:");
if(action == shareSEL){
return NO;
}
return YES;
}
// Tap gesture delegate method
- (void)singleTap:(UITapGestureRecognizer *)sender {
lastTouchPoint = [sender locationInView:self.webView];
[self becomeFirstResponder]; //added this line to fix the issue//
}
add a comment |
I Fixed it after some observation.
In -canPerformAction:withSender:
I am returning NO
for _share
and _define
options as I don't need them in my project. It works as expected on selection of word for first time, but shows up the options from second time.
Simple fix: Add [self becomeFirstResponder];
in tapGuesture
or Touch delegate methods
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
SEL defineSEL = NSSelectorFromString(@"_define:");
if(action == defineSEL){
return NO;
}
SEL shareSEL = NSSelectorFromString(@"_share:");
if(action == shareSEL){
return NO;
}
return YES;
}
// Tap gesture delegate method
- (void)singleTap:(UITapGestureRecognizer *)sender {
lastTouchPoint = [sender locationInView:self.webView];
[self becomeFirstResponder]; //added this line to fix the issue//
}
I Fixed it after some observation.
In -canPerformAction:withSender:
I am returning NO
for _share
and _define
options as I don't need them in my project. It works as expected on selection of word for first time, but shows up the options from second time.
Simple fix: Add [self becomeFirstResponder];
in tapGuesture
or Touch delegate methods
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
SEL defineSEL = NSSelectorFromString(@"_define:");
if(action == defineSEL){
return NO;
}
SEL shareSEL = NSSelectorFromString(@"_share:");
if(action == shareSEL){
return NO;
}
return YES;
}
// Tap gesture delegate method
- (void)singleTap:(UITapGestureRecognizer *)sender {
lastTouchPoint = [sender locationInView:self.webView];
[self becomeFirstResponder]; //added this line to fix the issue//
}
edited Mar 8 '16 at 23:54
Borys Verebskyi
3,31362234
3,31362234
answered Jan 14 '16 at 21:01
LaxLax
528
528
add a comment |
add a comment |
Here's my final solution, adapted from the solutions posted here. The key is to listen for the UIMenuControllerWillShowMenu
notification and then Dispatch.main.async
to hide the menu. This seems to do the trick to avoid the flashing menu.
My example uses a UITextField
, but it should be easily adapted to a WKWebView
.
class NoMenuTextField: UITextField {
override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
deregisterForMenuNotifications()
} else {
registerForMenuNotifications()
}
}
func registerForMenuNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.UIMenuControllerWillShowMenu,
object: nil,
queue: OperationQueue.main)
{ _ in
DispatchQueue.main.async {
UIMenuController.shared.setMenuVisible(false, animated: false)
UIMenuController.shared.update()
}
}
}
func deregisterForMenuNotifications() {
NotificationCenter.default.removeObserver(self,
name: Notification.Name.UIMenuControllerWillShowMenu,
object: nil)
}
}
add a comment |
Here's my final solution, adapted from the solutions posted here. The key is to listen for the UIMenuControllerWillShowMenu
notification and then Dispatch.main.async
to hide the menu. This seems to do the trick to avoid the flashing menu.
My example uses a UITextField
, but it should be easily adapted to a WKWebView
.
class NoMenuTextField: UITextField {
override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
deregisterForMenuNotifications()
} else {
registerForMenuNotifications()
}
}
func registerForMenuNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.UIMenuControllerWillShowMenu,
object: nil,
queue: OperationQueue.main)
{ _ in
DispatchQueue.main.async {
UIMenuController.shared.setMenuVisible(false, animated: false)
UIMenuController.shared.update()
}
}
}
func deregisterForMenuNotifications() {
NotificationCenter.default.removeObserver(self,
name: Notification.Name.UIMenuControllerWillShowMenu,
object: nil)
}
}
add a comment |
Here's my final solution, adapted from the solutions posted here. The key is to listen for the UIMenuControllerWillShowMenu
notification and then Dispatch.main.async
to hide the menu. This seems to do the trick to avoid the flashing menu.
My example uses a UITextField
, but it should be easily adapted to a WKWebView
.
class NoMenuTextField: UITextField {
override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
deregisterForMenuNotifications()
} else {
registerForMenuNotifications()
}
}
func registerForMenuNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.UIMenuControllerWillShowMenu,
object: nil,
queue: OperationQueue.main)
{ _ in
DispatchQueue.main.async {
UIMenuController.shared.setMenuVisible(false, animated: false)
UIMenuController.shared.update()
}
}
}
func deregisterForMenuNotifications() {
NotificationCenter.default.removeObserver(self,
name: Notification.Name.UIMenuControllerWillShowMenu,
object: nil)
}
}
Here's my final solution, adapted from the solutions posted here. The key is to listen for the UIMenuControllerWillShowMenu
notification and then Dispatch.main.async
to hide the menu. This seems to do the trick to avoid the flashing menu.
My example uses a UITextField
, but it should be easily adapted to a WKWebView
.
class NoMenuTextField: UITextField {
override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
deregisterForMenuNotifications()
} else {
registerForMenuNotifications()
}
}
func registerForMenuNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.UIMenuControllerWillShowMenu,
object: nil,
queue: OperationQueue.main)
{ _ in
DispatchQueue.main.async {
UIMenuController.shared.setMenuVisible(false, animated: false)
UIMenuController.shared.update()
}
}
}
func deregisterForMenuNotifications() {
NotificationCenter.default.removeObserver(self,
name: Notification.Name.UIMenuControllerWillShowMenu,
object: nil)
}
}
answered Apr 22 '18 at 22:35
LewisJiLewisJi
111
111
add a comment |
add a comment |
I tried the solution from Stephan Heilner but it didn't compile in Swift 4.
This is my implementation to disable the menuController in a WKWebView that works with Swift 4.
In my WKWebView subclass, I added these property and function :
var wkContentView: UIView? {
return self.subviewWithClassName("WKContentView")
}
private func swizzleResponderChainAction() {
wkContentView?.swizzlePerformAction()
}
Then, I added an extension in the same file, but out of the WKWebView subclass :
// MARK: - Extension used for the swizzling part linked to wkContentView (see above)
extension UIView {
/// Find a subview corresponding to the className parameter, recursively.
func subviewWithClassName(_ className: String) -> UIView? {
if NSStringFromClass(type(of: self)) == className {
return self
} else {
for subview in subviews {
return subview.subviewWithClassName(className)
}
}
return nil
}
func swizzlePerformAction() {
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
private func swizzleMethod(_ currentSelector: Selector, withSelector newSelector: Selector) {
if let currentMethod = self.instanceMethod(for: currentSelector),
let newMethod = self.instanceMethod(for:newSelector) {
let newImplementation = method_getImplementation(newMethod)
method_setImplementation(currentMethod, newImplementation)
} else {
print("Could not find originalSelector")
}
}
private func instanceMethod(for selector: Selector) -> Method? {
let classType = type(of: self)
return class_getInstanceMethod(classType, selector)
}
@objc private func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
And finally, I called the swizzleResponderChainAction()
function from the initializer (you can either override the designated initializer, or create a convenience one):
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
swizzleResponderChainAction()
}
Now, the WKWebView does not crash anymore when using a UIMenuController.
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
add a comment |
I tried the solution from Stephan Heilner but it didn't compile in Swift 4.
This is my implementation to disable the menuController in a WKWebView that works with Swift 4.
In my WKWebView subclass, I added these property and function :
var wkContentView: UIView? {
return self.subviewWithClassName("WKContentView")
}
private func swizzleResponderChainAction() {
wkContentView?.swizzlePerformAction()
}
Then, I added an extension in the same file, but out of the WKWebView subclass :
// MARK: - Extension used for the swizzling part linked to wkContentView (see above)
extension UIView {
/// Find a subview corresponding to the className parameter, recursively.
func subviewWithClassName(_ className: String) -> UIView? {
if NSStringFromClass(type(of: self)) == className {
return self
} else {
for subview in subviews {
return subview.subviewWithClassName(className)
}
}
return nil
}
func swizzlePerformAction() {
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
private func swizzleMethod(_ currentSelector: Selector, withSelector newSelector: Selector) {
if let currentMethod = self.instanceMethod(for: currentSelector),
let newMethod = self.instanceMethod(for:newSelector) {
let newImplementation = method_getImplementation(newMethod)
method_setImplementation(currentMethod, newImplementation)
} else {
print("Could not find originalSelector")
}
}
private func instanceMethod(for selector: Selector) -> Method? {
let classType = type(of: self)
return class_getInstanceMethod(classType, selector)
}
@objc private func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
And finally, I called the swizzleResponderChainAction()
function from the initializer (you can either override the designated initializer, or create a convenience one):
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
swizzleResponderChainAction()
}
Now, the WKWebView does not crash anymore when using a UIMenuController.
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
add a comment |
I tried the solution from Stephan Heilner but it didn't compile in Swift 4.
This is my implementation to disable the menuController in a WKWebView that works with Swift 4.
In my WKWebView subclass, I added these property and function :
var wkContentView: UIView? {
return self.subviewWithClassName("WKContentView")
}
private func swizzleResponderChainAction() {
wkContentView?.swizzlePerformAction()
}
Then, I added an extension in the same file, but out of the WKWebView subclass :
// MARK: - Extension used for the swizzling part linked to wkContentView (see above)
extension UIView {
/// Find a subview corresponding to the className parameter, recursively.
func subviewWithClassName(_ className: String) -> UIView? {
if NSStringFromClass(type(of: self)) == className {
return self
} else {
for subview in subviews {
return subview.subviewWithClassName(className)
}
}
return nil
}
func swizzlePerformAction() {
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
private func swizzleMethod(_ currentSelector: Selector, withSelector newSelector: Selector) {
if let currentMethod = self.instanceMethod(for: currentSelector),
let newMethod = self.instanceMethod(for:newSelector) {
let newImplementation = method_getImplementation(newMethod)
method_setImplementation(currentMethod, newImplementation)
} else {
print("Could not find originalSelector")
}
}
private func instanceMethod(for selector: Selector) -> Method? {
let classType = type(of: self)
return class_getInstanceMethod(classType, selector)
}
@objc private func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
And finally, I called the swizzleResponderChainAction()
function from the initializer (you can either override the designated initializer, or create a convenience one):
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
swizzleResponderChainAction()
}
Now, the WKWebView does not crash anymore when using a UIMenuController.
I tried the solution from Stephan Heilner but it didn't compile in Swift 4.
This is my implementation to disable the menuController in a WKWebView that works with Swift 4.
In my WKWebView subclass, I added these property and function :
var wkContentView: UIView? {
return self.subviewWithClassName("WKContentView")
}
private func swizzleResponderChainAction() {
wkContentView?.swizzlePerformAction()
}
Then, I added an extension in the same file, but out of the WKWebView subclass :
// MARK: - Extension used for the swizzling part linked to wkContentView (see above)
extension UIView {
/// Find a subview corresponding to the className parameter, recursively.
func subviewWithClassName(_ className: String) -> UIView? {
if NSStringFromClass(type(of: self)) == className {
return self
} else {
for subview in subviews {
return subview.subviewWithClassName(className)
}
}
return nil
}
func swizzlePerformAction() {
swizzleMethod(#selector(canPerformAction), withSelector: #selector(swizzledCanPerformAction))
}
private func swizzleMethod(_ currentSelector: Selector, withSelector newSelector: Selector) {
if let currentMethod = self.instanceMethod(for: currentSelector),
let newMethod = self.instanceMethod(for:newSelector) {
let newImplementation = method_getImplementation(newMethod)
method_setImplementation(currentMethod, newImplementation)
} else {
print("Could not find originalSelector")
}
}
private func instanceMethod(for selector: Selector) -> Method? {
let classType = type(of: self)
return class_getInstanceMethod(classType, selector)
}
@objc private func swizzledCanPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
And finally, I called the swizzleResponderChainAction()
function from the initializer (you can either override the designated initializer, or create a convenience one):
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
swizzleResponderChainAction()
}
Now, the WKWebView does not crash anymore when using a UIMenuController.
edited Jan 26 at 7:15
answered Jan 8 at 16:03
Frédéric AddaFrédéric Adda
3,81813857
3,81813857
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
add a comment |
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
I tried your solution in my code, but the UIMenuController not disabled.Still it appears on selection. Also the swizzleMethod not called.
– Srini
Jan 25 at 6:45
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Sorry, I forgot to precise that you have to call the new swizzled function from the initializer. That's fixed now. Thanks!
– Frédéric Adda
Jan 26 at 7:16
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
Thank you so much. it is working fine now
– Srini
Jan 29 at 7:15
add a comment |
One way that I've used is to simply disable the menu using CSS. The CSS property is called -webkit-touch-callout: none;
. You can apply it to the top level element and disable it for the whole page or any child element and disable it with more precision. Hope that helps.
add a comment |
One way that I've used is to simply disable the menu using CSS. The CSS property is called -webkit-touch-callout: none;
. You can apply it to the top level element and disable it for the whole page or any child element and disable it with more precision. Hope that helps.
add a comment |
One way that I've used is to simply disable the menu using CSS. The CSS property is called -webkit-touch-callout: none;
. You can apply it to the top level element and disable it for the whole page or any child element and disable it with more precision. Hope that helps.
One way that I've used is to simply disable the menu using CSS. The CSS property is called -webkit-touch-callout: none;
. You can apply it to the top level element and disable it for the whole page or any child element and disable it with more precision. Hope that helps.
answered Nov 19 '15 at 0:47
RyanRyan
3,71852943
3,71852943
add a comment |
add a comment |
pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// Add:
// Disable LongPress and Selection, no more UIMenucontroller
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil]; }
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
add a comment |
pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// Add:
// Disable LongPress and Selection, no more UIMenucontroller
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil]; }
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
add a comment |
pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// Add:
// Disable LongPress and Selection, no more UIMenucontroller
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil]; }
pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// Add:
// Disable LongPress and Selection, no more UIMenucontroller
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
[self.wkWebView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil]; }
answered Nov 29 '17 at 3:12
RomeRome
11
11
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
add a comment |
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
always show after doing this
– Shauket Sheikh
Oct 31 '18 at 3:18
add a comment |
In iOS 11, I have found a simple solution by an extension of WKWebView. I have not checked to see if this will work in earlier versions of iOS. The following is a simple example with one menu item.
import UIKit
import WebKit
extension WKWebView {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(highlightHandler):
return true
default:
return false
}
}
func createEditMenu() { // Should be called once
let highlight = UIMenuItem(title: "Highlight", action: #selector(highlightHandler))
menuItems.append(highlight)
UIMenuController.shared.menuItems = [highlight]
}
@objc func highlightHandler(sender: UIMenuItem) {
print("highlight clicked")
}
}
add a comment |
In iOS 11, I have found a simple solution by an extension of WKWebView. I have not checked to see if this will work in earlier versions of iOS. The following is a simple example with one menu item.
import UIKit
import WebKit
extension WKWebView {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(highlightHandler):
return true
default:
return false
}
}
func createEditMenu() { // Should be called once
let highlight = UIMenuItem(title: "Highlight", action: #selector(highlightHandler))
menuItems.append(highlight)
UIMenuController.shared.menuItems = [highlight]
}
@objc func highlightHandler(sender: UIMenuItem) {
print("highlight clicked")
}
}
add a comment |
In iOS 11, I have found a simple solution by an extension of WKWebView. I have not checked to see if this will work in earlier versions of iOS. The following is a simple example with one menu item.
import UIKit
import WebKit
extension WKWebView {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(highlightHandler):
return true
default:
return false
}
}
func createEditMenu() { // Should be called once
let highlight = UIMenuItem(title: "Highlight", action: #selector(highlightHandler))
menuItems.append(highlight)
UIMenuController.shared.menuItems = [highlight]
}
@objc func highlightHandler(sender: UIMenuItem) {
print("highlight clicked")
}
}
In iOS 11, I have found a simple solution by an extension of WKWebView. I have not checked to see if this will work in earlier versions of iOS. The following is a simple example with one menu item.
import UIKit
import WebKit
extension WKWebView {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(highlightHandler):
return true
default:
return false
}
}
func createEditMenu() { // Should be called once
let highlight = UIMenuItem(title: "Highlight", action: #selector(highlightHandler))
menuItems.append(highlight)
UIMenuController.shared.menuItems = [highlight]
}
@objc func highlightHandler(sender: UIMenuItem) {
print("highlight clicked")
}
}
answered Nov 16 '18 at 7:49
Gary NorrisGary Norris
112
112
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f33243230%2fdisable-entire-uimenucontroller-edit-menu-in-wkwebview%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Out of curiosity have you filed a bug report with Apple for this?
– Ryan
Nov 19 '15 at 0:48
@Ryan, no not yet. Thanks for your answer below. I just tried it and I'm still seeing the menu when I long press on text under iOS 9 on my iPhone 6. Have you tried it with WKWebView on iOS? I verified with Web Inspector that the new CSS setting is applied to body.
– davew
Nov 19 '15 at 1:58
@davew any success with this?
– Jed Grant
Nov 29 '15 at 1:48
@JedGrant no, I've been living with the standard Editing menu for now. On Nov 19, I briefly tried Ryan's suggestion, but didn't get it to work and haven't had time to dig deeper.
– davew
Dec 1 '15 at 21:01