Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disabling user selection in UIWebView

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

like image 691
Engin Kurutepe Avatar asked May 13 '11 16:05

Engin Kurutepe


11 Answers

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];
}
like image 90
WrightsCS Avatar answered Oct 01 '22 01:10

WrightsCS


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'")!
}
like image 44
TPoschel Avatar answered Oct 04 '22 01:10

TPoschel


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

}
like image 24
Johno Scott Avatar answered Oct 02 '22 01:10

Johno Scott


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>
like image 32
pablobart Avatar answered Oct 01 '22 01:10

pablobart


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.

like image 37
Bittu Avatar answered Oct 04 '22 01:10

Bittu


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';"];
}
like image 39
pawelini1 Avatar answered Oct 04 '22 01:10

pawelini1


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 */
     }
like image 34
Narsingh Tomar Avatar answered Sep 30 '22 01:09

Narsingh Tomar


    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.

like image 36
Samrat Pramanik Avatar answered Oct 02 '22 01:10

Samrat Pramanik


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;
}
like image 26
Dmitry Avatar answered Oct 04 '22 01:10

Dmitry


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!!

like image 25
Scooter Avatar answered Oct 01 '22 01:10

Scooter


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
like image 35
Kaiyuan Xu Avatar answered Oct 03 '22 01:10

Kaiyuan Xu