禁用UIWebView中的用户选择

124

我有一个应用程序,其中我将内容加载到UIWebView并进行呈现。 我无法完全禁用用户交互,因为我希望用户能够单击链接。 我只需要禁用用户选择。 我在互联网上找到了一些地方,您可以使用以下内容:

webView.userInteractionEnabled = NO;
for (UIView *subview in [webView subviews]) {
    if ([subview isKindOfClass:[UIScrollView class]]) {
        ((UIScrollView *)subview).scrollEnabled = NO;
    }
}
document.body.style.webkitUserSelect='none';

我尝试将这个插入为

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

webViewDidFinishLoad:方法中,但是它并没有起作用。我仍然能够选择和复制WebView内的文本。

有任何想法是出了什么问题吗?

更新:这只发生在iOS 4.3及以上版本。

12个回答

281

这里有几种禁止选择的方法:

在你的移动Web文档中添加以下内容

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

以编程方式加载以下Javascript代码:

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

禁用复制/粘贴用户菜单:

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

1
使用分类扩展 UIWebView - Deepak Danduprolu
4
Engin,第一个方法在移动版Safari iOS 4.3.1上有效。但WrightCS忘记添加选择器。要将其应用于所有元素,请使用星号,如:* {/css放置在此/}。 - fisherwebdev
1
在我的PhoneGap应用程序中,添加样式完美地去除了在链接上长按时出现的菜单。 - spatical
7
canPerformAction不能禁用剪切、复制、粘贴菜单,以及选择、全选菜单。如何解决?-webkit-touch-calloutremoveAllRanges会破坏用户输入。无法使用所有提供的方法:( - Dmitry
4
把这些非选择样式放在 * 全局范围内的问题是会阻止网络表单中的用户输入。我发现将其放在 body 标签内部可以获得更好的结果。 - David Douglas
显示剩余4条评论

105

我可以确认以下代码在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';"];
}

这也适用于iOS 9及更高版本。以下是swift代码:

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

2
已在iOS 7上确认!注意:通过连接两个字符串,可以将这两个调用组合成一个。 - Maen
在iOS 9.x上,即使使用了above属性,在长按后屏幕顶部仍会出现一个空的call out。 - DwarDoh
我只会JS,不知道如何编辑Objective C代码或更改PhoneGap插件。有人能帮忙吗?我该如何使用上面的代码? - Lakshay Dulani
与 iOS 9 和 10 完美兼容的专业人员! - ΩlostA

26

我正在一个使用Trigger.IO打包的安卓/iPhone web应用中使用这种技术,发现只有在:not()伪类的链式语法下才能正常工作:

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

}

我正在使用jQuery Mobile和Phonegap Build,这对我很有效。我首先尝试了 *:not(input,textarea) {-webkit-touch-callout: none; -webkit-user-select: none;},但这对我没有起作用。谢谢! - Mark Rummel

18

我喜欢WrightsCS的解决方案,但我会使用这个方案,以便用户仍然可以在输入框中执行复制、粘贴和选择等操作。

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

2
如果你要这样做,请不要忘记文本区域! - Ryan

9

我不确定设置是如何完成的,但为什么不在 viewWillDisappear 被调用时清除 pasteBoard 呢?也许可以像这样在 appDelegate.m 中进行:

[UIPasteboard generalPasteboard].string = nil;

这将确保用户复制的任何数据都无法在应用程序之外粘贴。此外,就像Engin所说的那样,您可以在包含UIWebView的控制器类中覆盖canPerformSelector方法。


1
这是最好的解决方案之一。可以在 - (void)applicationDidEnterBackground:(UIApplication *)application 事件处理程序中设置。这可以确保没有数据流出应用程序。 - RK-
@Krishan,你百分之百确定这也能阻止截屏吗? - Norman H
我喜欢这个解决方案,但由于它在应用程序处于活动状态时停留在剪贴板上,因此在后台线程中运行的另一个(恶意)应用程序可以在剪贴板上有数据时访问通用剪贴板。 - binary_falcon
这样做是否也会删除应用程序之外的数据?也许在出现时获取数据并在消失时将其设置回去会更好。 - Hamzah Malik

7
TPoschel的答案是正确的,但在我的情况下,顺序很重要。
// 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';"];
}

7

我可以确认这一定会对你有用。

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

如果您只想禁用锚点按钮标签,请使用以下方法。
    a {-webkit-user-select: none; /* disable selection/Copy of UIWebView */
   -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */
     }

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

只需将以下代码添加到您的viewDidLoad()方法中即可。用户可以点击链接,但无法复制内容。


5

一周辛勤工作的成果!如果你想在多个页面上保存鼠标事件和用户输入,其他所有答案都是不正确的。

1)Swizzle方法(通过rentzsch/jrswizzle库):

[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) 将 UIWebView 放置在 UIView 上并添加以下代码:

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

还有这个:

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

2
对于这个程序的一些解释会很好。特别是,我对你的手势为什么叫做"singleTap"但却需要两次点击感到困惑。 - arlomedia

4

第一个解决方案对我非常有效......直到我将一个.pdf文件加载到我的UIWebView中。

加载.doc文件非常完美,但是加载.pdf文件会导致以下代码行不再产生预期效果,并且在用户长按时再次弹出复制/定义菜单。

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

在经历了一番抓狂之后,我在这里找到了Johnny Rockex的回答,并且它非常有效。 UIWebView without Copy/Paste when displaying PDF files

非常感谢他提供这个易于实现、天才般的解决方案!!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接