Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove Bold, Italic, and Underline options from the UIWebView in iOS

Tags:

ios

I cannot seem to remove Bold, Italic, and Underline options from the UIWebView. Why is this not possible?

CustomWebView.h:

#import <UIKit/UIKit.h>

@interface CustomUIWebView : UIWebView

@end

CustomWebView.m:

#import <Foundation/Foundation.h>
#import "CustomUIWebView.h"

@implementation CustomUIWebView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    return NO;
    return [super canPerformAction:action withSender:sender];
}

@end

enter image description here

like image 828
Josh O'Connor Avatar asked Aug 06 '18 22:08

Josh O'Connor


1 Answers

I've investigated a lot to solve this problem and finally found a solution. I first though overriding - (BOOL)canPerformAction:(SEL)action withSender:(id)sender in the UIViewController containing the UIWebView would be sufficient to prevent text style options to show up. But unfortunately, it wasn't as easy as it seems.

The main reason for this is that we have to override the canPerformAction of the main first responder. Calling [[[UIApplication sharedApplication] keyWindow] performSelector:@selector(firstResponder)] from the controller informs us that UIWebBrowserView is the actual main first responder. We would want to subclass UIWebBrowserView, but since it's a private class, we might have our app rejected by Apple during review process. This answer from @Shayan RC suggested to perform a method swizzling to allow kind of overriding this method without subclassing UIWebBrowserView (and thus prevent from App Store rejection).


SOLUTION :

The idea is to add a new method that would replace canPerformAction. I've created an array with all methods signatures that we want to keep in the Menu. To remove styling options, we just have to not add @"_showTextStyleOptions:" to this array. Add every other method signature that you want to show (I've added a NSLog of the signatures, so that you can pick the ones you want).

- (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;
}

Now we can perform method swizzling to call previous method instead of canPerformAction, with following method (from the answer of @Shayan RC). This will require to add #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);
            }
        }
    }
}

Finally, call previous method in viewDidLoad like so : [self replaceUIWebBrowserView:_webView].

It could seems hard with method swizzling, but it allows you to keep the code in your view controller. Please tell me if you encounter any difficulties in implementing previous code.

NOTE : This behavior is much more easy to implement with WKWebView than with UIWebView, and UIWebView is deprecated, you should really consider switching to WKWebView.

like image 104
AnthoPak Avatar answered Oct 22 '22 22:10

AnthoPak