为了解决这个问题,我进行了很多调查,最终找到了解决方案。我首先虽然在包含UIWebView 的UIViewController 中覆盖- (BOOL)canPerformAction:(SEL)action withSender:(id)sender 足以防止出现文本样式选项。但不幸的是,这并不像看起来那么容易。
这样做的主要原因是我们必须覆盖主要第一响应者的canPerformAction。从控制器调用[[[UIApplication sharedApplication] keyWindow] performSelector:@selector(firstResponder)] 会告诉我们UIWebBrowserView 是实际的主要第一响应者。我们想要子类UIWebBrowserView,但由于它是一个私有类,我们的应用程序可能会在审核过程中被 Apple 拒绝。来自@Shayan RC 的This answer 建议执行一种方法调配,以允许在不继承UIWebBrowserView 的情况下覆盖此方法(从而防止App Store 拒绝)。
解决方案:
这个想法是添加一个新方法来替换canPerformAction。我创建了一个数组,其中包含我们想要保留在菜单中的所有方法签名。要删除样式选项,我们只需要不将 @"_showTextStyleOptions:" 添加到此数组中。添加您想要显示的所有其他方法签名(我添加了签名的NSLog,以便您可以选择您想要的)。
- (BOOL) mightPerformAction:(SEL)action withSender:(id)sender {
NSLog(@"action : %@", NSStringFromSelector(action));
NSArray<NSString*> *selectorsToKeep = @[@"cut:", @"copy:", @"select:", @"selectAll:", @"_lookup:"]; //add in this array every action you want to keep
if ([selectorsToKeep containsObject:NSStringFromSelector(action)]) {
return YES;
}
return NO;
}
现在我们可以使用以下方法(来自@Shayan RC 的答案)执行方法调配来调用先前的方法而不是canPerformAction。这需要添加#import <objc/runtime.h>。
- (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);
}
}
}
}
最后,像这样调用viewDidLoad 中的前一个方法:[self replaceUIWebBrowserView:_webView]。
方法调配似乎很难,但它允许您将代码保留在视图控制器中。如果您在实现以前的代码时遇到任何困难,请告诉我。
注意:使用WKWebView 比使用UIWebView 更容易实现此行为,并且不推荐使用UIWebView,您真的应该考虑切换到WKWebView。