This seems to be one of the most frequently discussed topics here but I couldn\'t find a solution which actually works. I\'m posting this question to share a solution which
Also you can hide menu:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillBeShown:) name:UIMenuControllerWillShowMenuNotification object:nil];
...
- (void)menuWillBeShown:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(),^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
});
}
The essential trick here is dispatch_async
.
The root cause of the first solution not working is the subview called UIWebBrowserView
. This seems to be the view whose canPerformAction returns true for any action
displayed in the context menu.
Since this UIWebBrowserView
is a private class we shouldn't try to subclass it (because it will get your app rejected).
So what we do instead is we make another method called mightPerformAction:withSender:
, like so-
- (BOOL)mightPerformAction:(SEL)action withSender:(id)sender {
NSLog(@"******Action!! %@******",NSStringFromSelector(action));
if (action == @selector(copy:))
{
NSLog(@"Copy Selector");
return NO;
}
else if (action == @selector(cut:))
{
NSLog(@"cut Selector");
return NO;
}
else if (action == NSSelectorFromString(@"_define:"))
{
NSLog(@"define Selector");
return NO;
}
else if (action == @selector(paste:))
{
NSLog(@"paste Selector");
return NO;
}
else
{
return [super canPerformAction:action withSender:sender];
}
}
and add another method to replace canPerformAction:withSender: with mightPerformAction:withSender:
- (void) replaceUIWebBrowserView: (UIView *)view
{
//Iterate through subviews recursively looking for UIWebBrowserView
for (UIView *sub in view.subviews) {
[self replaceUIWebBrowserView:sub];
if ([NSStringFromClass([sub class]) isEqualToString:@"UIWebBrowserView"]) {
Class class = sub.class;
SEL originalSelector = @selector(canPerformAction:withSender:);
SEL swizzledSelector = @selector(mightPerformAction:withSender:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self.class, swizzledSelector);
//add the method mightPerformAction:withSender: to UIWebBrowserView
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//replace canPerformAction:withSender: with mightPerformAction:withSender:
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
}
}
And finally call it in the viewDidLoad of the ViewController:
[self replaceUIWebBrowserView:self.webView];
Note: Add #import <objc/runtime.h>
to your viewController then error(Method) will not shown.
Note: I am using NSSelectorFromString
method to avoid detection of private API selectors during the review process.
This seems to be working fine in iOS7 with Xcode 5, if anyone can find any issues in this please let me know how I can improve it..