Disabling user selection in UIWebView

IosUiwebviewWebkitIos4Mobile Webkit

Ios Problem Overview


I have an app where I load content to a UIWebView and present this. I cannot disable user interaction completely because I want the user to be able to click links. I just need to disable user selection. I found somewhere in the Internets that you can use:

document.body.style.webkitUserSelect='none';

I tried inserting this as

[self.contentView stringByEvaluatingJavaScriptFromString:@"document.body.style.webkitUserSelect='none';"]; 

in webViewDidFinishLoad:

However, it does not work. I am still able to select and copy text inside the WebView.

Any Ideas what might be going wrong?

Update: This only seems to happen starting with iOS 4.3

Ios Solutions


Solution 1 - Ios

Here are a few ways to disable selection:

Add the following to your mobile web documents

<style type="text/css">
* {
    -webkit-touch-callout: none;
    -webkit-user-select: none; /* Disable selection/copy in UIWebView */
}
</style>

Programmatically load the following Javascript code:

NSString * jsCallBack = @"window.getSelection().removeAllRanges();";    
[webView stringByEvaluatingJavaScriptFromString:jsCallBack];

Disable the Copy / Paste user menu:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender 
{    
    if (action == @selector(copy:) ||
        action == @selector(paste:)||
        action == @selector(cut:)) 
    {
        return _copyCutAndPasteEnabled;
    }
    return [super canPerformAction:action withSender:sender];
}

Solution 2 - Ios

I can confirm that the following code works in iOS 5.0 - 8.0.

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    // Disable user selection
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
    // Disable callout
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
}

Also works for iOS 9 and later. Here's the swift code:

func webViewDidFinishLoad(webView: UIWebView) {
    // Disable user selection
    webView.stringByEvaluatingJavaScriptFromString("document.documentElement.style.webkitUserSelect='none'")!
    // Disable callout
    webView.stringByEvaluatingJavaScriptFromString("document.documentElement.style.webkitTouchCallout='none'")!
}

Solution 3 - Ios

I am using this technique in a web app for Android / iPhone (packaged with Trigger.IO) and found it would only work with the chaining syntax for the :not() pseudo-class, :

*:not(input):not(textarea) {
-webkit-user-select: none; /* disable selection/Copy of UIWebView */
    -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */

}

Solution 4 - Ios

I like the WrightsCS solution but I will use this so the users can still using the copy,paste and select actions on inputs

<style type="text/css">
*:not(input,textarea) {
    -webkit-touch-callout: none;
    -webkit-user-select: none; /* Disable selection/Copy of UIWebView */
}
</style>

Solution 5 - Ios

I am not sure how the setup is done, but why dont you just clear the pasteBoard when viewWillDisappear is called. Maybe something like in your appDelegate.m:

[UIPasteboard generalPasteboard].string = nil;

this will make sure whatever data user might have copied, they will not be able to paste it outside of the app.

Also, like Engin said you can override the canPerformSelector method in the controller class that contains the uiwebview.

Solution 6 - Ios

TPoschel answer is corrent but in my case order was important.

// this works - locks selection and callout
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
}

// this doesn't work - locks only callout
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
}

Solution 7 - Ios

I can confirm this will definitely work for you.

<style type="text/css">
  *:not(input):not(textarea) {
   -webkit-user-select: none; /* disable selection/Copy of UIWebView */
   -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */
   }       
</style>

If you want Disable only anchor button tag use this.

    a {-webkit-user-select: none; /* disable selection/Copy of UIWebView */
   -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */
     }

Solution 8 - Ios

    let longPress:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: nil, action: nil)
    longPress.minimumPressDuration = 0.2
    webView.addGestureRecognizer(longPress)

Simply add this code to your viewDidLoad(). User can click on link but can not copy the content.

Solution 9 - Ios

Result of the great work for one week! All others answers are incorrect if you want to save mouse events and user input on many pages.

  1. Swizzle method (by rentzsch/jrswizzle library):

    [NSClassFromString(@"UIWebDocumentView") jr_swizzleMethod:@selector(canPerformAction:withSender:) withMethod:@selector(myCanPerformAction:withSender:) error:nil];

NSObject+myCanPerformAction.h:

@interface NSObject (myCanPerformAction)

- (BOOL)myCanPerformAction:(SEL)action withSender:(id)sender;

@end

NSObject+myCanPerformAction.m:

#import "NSObject+myCanPerformAction.h"

@implementation NSObject (myCanPerformAction)

- (BOOL)myCanPerformAction:(SEL)action withSender:(id)sender {
    if (action == @selector(copy:)) {
        return [self myCanPerformAction:action withSender:sender];
    }
    if (action == @selector(paste:)) {
        return [self myCanPerformAction:action withSender:sender];
    }
    return NO;
}

@end

2) Place UIWebView on UIView and add a code:

    UITapGestureRecognizer* singleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)] autorelease];
    singleTap.numberOfTapsRequired = 2;
    singleTap.numberOfTouchesRequired = 1;
    singleTap.delegate = self;
    [self.view addGestureRecognizer:singleTap];

And this one:

- (void)handleSingleTap:(UIGestureRecognizer*)gestureRecognizer {
    return;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if ([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        UITapGestureRecognizer *gesture = (UITapGestureRecognizer *)otherGestureRecognizer;
        if (gesture.numberOfTapsRequired == 2) {
            [otherGestureRecognizer.view removeGestureRecognizer:otherGestureRecognizer];
        }
    }
    return YES;
}

Solution 10 - Ios

The first solution given worked perfectly for me...until I loaded a .pdf into my UIWebView.

Loading a .doc file worked perfectly, but loading a .pdf resulted in the following line of code no longer having the desired effect and the copy/define menu popped up again on a long touch by the user.

    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];

After another bout of hair pulling I found this answer on here by Johnny Rockex and it worked like a champ. https://stackoverflow.com/questions/12768194/uiwebview-without-copy-paste-when-displaying-pdf-files

Many thanks to him for this easy to implement, genius solution!!

Solution 11 - Ios

For me, I have intended to fetch the images' NSData from UIWebView by LongPressGesture.

But the Magnifier and Copy/Paste/Cut always occur before my func execute.

And I found this: enter image description here

It means, the the Magnifier and Copy/Paste/Cut need 0.5s to execute, so if your func can be executed in 0.49s, DONE !

self.longPressPan.minimumPressDuration = 0.3

Solution 12 - Ios

Use the web view interection function

   webView.userInteractionEnabled = false

It works for me

PS: remember to enable the interaction back when you want the user could interact with the webview again

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEngin KurutepeView Question on Stackoverflow
Solution 1 - IosWrightsCSView Answer on Stackoverflow
Solution 2 - IosTPoschelView Answer on Stackoverflow
Solution 3 - IosJohno ScottView Answer on Stackoverflow
Solution 4 - IospablobartView Answer on Stackoverflow
Solution 5 - IosBittuView Answer on Stackoverflow
Solution 6 - Iospawelini1View Answer on Stackoverflow
Solution 7 - IosNarsingh TomarView Answer on Stackoverflow
Solution 8 - IosSamrat PramanikView Answer on Stackoverflow
Solution 9 - IosDmitryView Answer on Stackoverflow
Solution 10 - IosScooterView Answer on Stackoverflow
Solution 11 - IosKaiyuan XuView Answer on Stackoverflow
Solution 12 - IosSrohrView Answer on Stackoverflow