124

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

Bhavin Ramani
  • 3,221
  • 5
  • 30
  • 41
Engin Kurutepe
  • 6,719
  • 3
  • 34
  • 64

12 Answers12

281

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];
}
WrightsCS
  • 50,551
  • 22
  • 134
  • 186
  • Hi Wright, unfortunately the first one does not work in iOS 4.3. Where should I implement the second one? I tried `webViewDidFinishLoad:` but it didn't work. I also tried the third method in my view controller who is responsible for the UIWebView. It keep returning `NO` but this doesn't prevent the UIWebView from showing the menu. – Engin Kurutepe May 19 '11 at 21:38
  • 1
    Extend `UIWebView` using a category. – Deepak Danduprolu May 23 '11 at 02:06
  • 4
    Engin, the first one does work in Mobile Safari, iOS 4.3.1. But WrightCS forgot to add the selector. To apply it to all elements, use the asterisk, like: * { /*css goes here*/ } – fisherwebdev Aug 04 '11 at 06:15
  • 1
    Adding the style worked perfectly in removing the menu that appeared when tapholding on a link inside of my PhoneGap app. – spatical Sep 09 '11 at 04:39
  • 7
    `canPerformAction` doesn't disable Cut, Copy, Paste menu. And Select, Select All menu. How to fix this? `-webkit-touch-callout` and `removeAllRanges` break user input. Can't use all provided methods:( – Dmitry Nov 07 '12 at 00:42
  • How to add this in my document Shall i do copy paste in the document – SwathiK Apr 24 '15 at 07:24
  • 4
    The problem with putting these no select styles in `*` global scope is that it stops user input in web forms. I found I got better results by placing inside the `body` tag instead. – David Douglas Aug 12 '15 at 15:12
  • @DeepakDanduprolu I have extended `UIWebView` and used the debugger to make sure it reaches to the function and it returns `NO`, but copy paste and cut still show up on the menu. Do you have an example possibly? – bleepmeh Sep 20 '18 at 00:39
  • Save my time, Thank you!! – Chris Ho Feb 18 '20 at 03:01
105

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'")!
}
Husam
  • 8,149
  • 3
  • 38
  • 45
TPoschel
  • 3,775
  • 2
  • 30
  • 29
26

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 */

}
Johno Scott
  • 1,730
  • 18
  • 13
  • I'm using jQuery Mobile and Phonegap Build and this worked for me. I first tried, `*:not(input,textarea) {-webkit-touch-callout: none; -webkit-user-select: none;}`, but that didn't do the trick for me. Thanks! – Mark Rummel Feb 07 '13 at 04:22
18

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>
pablobart
  • 2,641
  • 1
  • 24
  • 22
9

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.

Bittu
  • 676
  • 2
  • 8
  • 22
  • 1
    This is one of the best solutions. This can be set in - (void)applicationDidEnterBackground:(UIApplication *)application event handler. And this ensures that no data goes out of the App. – RK- Sep 26 '11 at 15:01
  • @Krishan, are you 100% certain that will stop screenshots as well? – Norman H Jul 22 '13 at 20:23
  • I like this solution, but since it stays on the pasteboard while the application is active, another (malicious) application running in the background thread can access the general pasteboard while it has data in it. – binary_falcon Sep 26 '14 at 15:33
  • wouldnt this also remove data not from the app? Perhaps get the data at appear and set it back to that on disappear would work better – Hamzah Malik Oct 14 '17 at 17:27
7

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';"];
}
pawelini1
  • 487
  • 7
  • 15
7

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 */
     }
Narsingh Tomar
  • 487
  • 5
  • 15
6
    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.

Samrat Pramanik
  • 201
  • 3
  • 8
5

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;
}
soumya
  • 3,801
  • 9
  • 35
  • 69
Dmitry
  • 14,306
  • 23
  • 105
  • 189
  • 2
    Some explanation of what this is supposed to do would be nice. In particular, I'm confused about why your gesture is called singleTap but requires two taps. – arlomedia Dec 12 '14 at 07:14
4

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. UIWebView without Copy/Paste when displaying PDF files

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

Community
  • 1
  • 1
Scooter
  • 4,068
  • 4
  • 32
  • 47
2

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
soumya
  • 3,801
  • 9
  • 35
  • 69
Kaiyuan Xu
  • 780
  • 7
  • 13
-4

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

soumya
  • 3,801
  • 9
  • 35
  • 69
Srohr
  • 11
  • 3
  • 1
    Welcome to stackoverflow. Does answer really add value to this old question? Please review [answer]. – dbank Mar 13 '15 at 14:26
  • 1
    Consider expanding your answer (providing some small code example) to increase the value of your answer. Also, answers containing 'it works for me' don't really inspire confidence - better to present the answer as-is and then troubleshoot if someone asks for clarification on your answer. – Aaron D Mar 14 '15 at 04:40