Désactivation de la sélection des utilisateurs dans UIWebView

121

J'ai une application dans laquelle je charge du contenu UIWebViewet le présente. Je ne peux pas désactiver complètement l'interaction de l'utilisateur car je souhaite que l'utilisateur puisse cliquer sur des liens. J'ai juste besoin de désactiver la sélection des utilisateurs. J'ai trouvé quelque part dans les internets que vous pouvez utiliser:

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

J'ai essayé d'insérer ceci comme

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

dans webViewDidFinishLoad:

Cependant, cela ne fonctionne pas. Je suis toujours en mesure de sélectionner et de copier du texte dans WebView.

Des idées sur ce qui pourrait mal tourner?

Mise à jour: cela ne semble se produire qu'à partir d'iOS 4.3

Moteur Kurutepe
la source

Réponses:

280

Voici quelques façons de désactiver la sélection:

Ajoutez les éléments suivants à vos documents Web mobiles

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

Chargez par programme le code Javascript suivant:

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

Désactivez le menu utilisateur Copier / Coller:

- (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
la source
1
Étendez à l' UIWebViewaide d'une catégorie.
Deepak Danduprolu
4
Engin, le premier fonctionne dans Mobile Safari, iOS 4.3.1. Mais WrightCS a oublié d'ajouter le sélecteur. Pour l'appliquer à tous les éléments, utilisez l'astérisque, comme: * {/ * css va ici * /}
fisherwebdev
1
L'ajout du style a parfaitement fonctionné en supprimant le menu qui apparaissait lors de la saisie sur un lien à l'intérieur de mon application PhoneGap.
spatial du
7
canPerformActionne désactive pas le menu Couper, Copier, Coller. Et sélectionnez, sélectionnez tout le menu. Comment régler ceci? -webkit-touch-calloutet removeAllRangesinterrompre l'entrée de l'utilisateur. Impossible d'utiliser toutes les méthodes fournies :(
Dmitry
4
Le problème de placer ces styles sans sélection dans *une portée globale est que cela arrête la saisie de l'utilisateur dans les formulaires Web. J'ai trouvé que j'avais de meilleurs résultats en plaçant bodyplutôt à l'intérieur de l' étiquette.
David Douglas
104

Je peux confirmer que le code suivant fonctionne dans 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';"];
}

Fonctionne également pour iOS 9 et versions ultérieures. Voici le code SWIFT:

func webViewDidFinishLoad(webView: UIWebView) {
    // Disable user selection
    webView.stringByEvaluatingJavaScriptFromString("document.documentElement.style.webkitUserSelect='none'")!
    // Disable callout
    webView.stringByEvaluatingJavaScriptFromString("document.documentElement.style.webkitTouchCallout='none'")!
}
TPoschel
la source
2
Confirmé sur iOS 7! Remarque: il est possible de regrouper les deux appels en un en concaténant les deux chaînes.
Bigood
Sur iOS 9.x, un appel vide apparaît en haut de l'écran après un appui long, même lors de l'utilisation ci-dessus.
DwarDoh
comment puis-je utiliser le code ci-dessus .. Je sais juste JS; Je n'ai aucune idée de la façon dont je peux modifier le code objectif c ou changer le plugin phonegap..si quelqu'un peut aider
Lakshay
Travaillez avec iOS 9 & 10 Perfect Guy!
ΩlostA
25

J'utilise cette technique dans une application Web pour Android / iPhone (fournie avec Trigger.IO) et j'ai trouvé qu'elle ne fonctionnerait qu'avec la syntaxe de chaînage pour la pseudo-classe: 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 */

}
Johno Scott
la source
J'utilise jQuery Mobile et Phonegap Build et cela a fonctionné pour moi. J'ai d'abord essayé, *:not(input,textarea) {-webkit-touch-callout: none; -webkit-user-select: none;}mais cela n'a pas fonctionné pour moi. Merci!
Mark Rummel
18

J'aime la solution WrightsCS mais je vais l'utiliser pour que les utilisateurs puissent toujours utiliser les actions copier, coller et sélectionner sur les entrées

<style type="text/css">
*:not(input,textarea) {
    -webkit-touch-callout: none;
    -webkit-user-select: none; /* Disable selection/Copy of UIWebView */
}
</style>
pablobart
la source
2
Si vous comptez faire cela, n'oubliez pas textarea!
Ryan
9

Je ne sais pas comment la configuration est effectuée, mais pourquoi ne pas effacer le pasteBoard lorsque viewWillDisappear est appelé. Peut-être quelque chose comme dans votre appDelegate.m:

[UIPasteboard generalPasteboard].string = nil;

cela garantira que toutes les données que l'utilisateur aura copiées, il ne pourra pas les coller en dehors de l'application.

De plus, comme Engin l'a dit, vous pouvez remplacer la méthode canPerformSelector dans la classe de contrôleur qui contient uiwebview.

Bittu
la source
1
C'est l'une des meilleures solutions. Cela peut être défini dans - (void) applicationDidEnterBackground: (UIApplication *) gestionnaire d'événements d'application. Et cela garantit qu'aucune donnée ne sort de l'application.
Krishnan le
@Krishan, êtes-vous sûr à 100% que les captures d'écran seront également interrompues?
Norman H
J'aime cette solution, mais comme elle reste sur la table de montage pendant que l'application est active, une autre application (malveillante) exécutée dans le thread d'arrière-plan peut accéder à la table de montage générale tant qu'elle contient des données.
binary_falcon
cela ne supprimerait-il pas également les données de l'application? Peut-être qu'obtenir les données à apparaître et les remettre à celles sur disparaître fonctionnerait mieux
Hamzah Malik
7

La réponse de TPoschel est correcte mais dans mon cas, l'ordre était 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
la source
7

Je peux confirmer que cela fonctionnera certainement pour vous.

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

Si vous souhaitez désactiver uniquement la balise de bouton d'ancrage, utilisez-la.

    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
la source
5

Résultat de l'excellent travail d'une semaine! Toutes les autres réponses sont incorrectes si vous souhaitez enregistrer les événements de souris et les entrées utilisateur sur de nombreuses pages.

1) Méthode Swizzle (par la bibliothèque 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) Placez UIWebView sur UIView et ajoutez un code:

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

Et celui-là:

- (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;
}
Dmitry
la source
2
Une explication de ce que cela est censé faire serait bien. En particulier, je ne comprends pas pourquoi votre geste s'appelle singleTap mais nécessite deux pressions.
arlomedia
5
    let longPress:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: nil, action: nil)
    longPress.minimumPressDuration = 0.2
    webView.addGestureRecognizer(longPress)

Ajoutez simplement ce code à votre viewDidLoad (). L'utilisateur peut cliquer sur le lien mais ne peut pas copier le contenu.

Samrat Pramanik
la source
4

La première solution donnée fonctionnait parfaitement pour moi ... jusqu'à ce que je charge un .pdf dans mon UIWebView.

Le chargement d'un fichier .doc fonctionnait parfaitement, mais le chargement d'un .pdf faisait que la ligne de code suivante n'avait plus l'effet souhaité et le menu copier / définir réapparaissait après une longue pression de la part de l'utilisateur.

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

Après un autre coup de cheveux, j'ai trouvé cette réponse ici par Johnny Rockex et cela a fonctionné comme un champion. UIWebView sans copier / coller lors de l'affichage de fichiers PDF

Un grand merci à lui pour cette solution géniale facile à mettre en œuvre !!

Scooter
la source
2

Pour moi, j'ai eu l'intention de récupérer les images NSDatade UIWebViewparLongPressGesture .

Mais la loupe et le copier / coller / couper se produisent toujours avant l'exécution de ma fonction.

Et j'ai trouvé ceci: entrez la description de l'image ici

Cela signifie que la Loupe et le Copier / Coller / Couper ont besoin de 0,5 s pour s'exécuter, donc si votre func peut être exécutée en 0,49 s, FAIT!

self.longPressPan.minimumPressDuration = 0.3
Kaiyuan Xu
la source
cela fonctionne pour moi dans iOS 9 + Xcode 7 GM, les autres méthodes que j'ai essayées sont simplement utiles dans iOS 7 et 8
Kaiyuan Xu
-4

Utiliser la fonction d'interection de vue Web

   webView.userInteractionEnabled = false

Ça marche pour moi

PS: n'oubliez pas de réactiver l'interaction lorsque vous souhaitez que l'utilisateur puisse à nouveau interagir avec la vue Web

Srohr
la source
1
Bienvenue dans stackoverflow. La réponse ajoute-t-elle vraiment de la valeur à cette vieille question? Veuillez consulter Comment répondre .
dbank
1
Envisagez d'élargir votre réponse (en fournissant un petit exemple de code) pour augmenter la valeur de votre réponse. De plus, les réponses contenant «ça marche pour moi» n'inspirent pas vraiment confiance - mieux vaut présenter la réponse telle quelle, puis résoudre le problème si quelqu'un vous demande des éclaircissements sur votre réponse.
Aaron D