web-dev-qa-db-fra.com

UIGestureRecognizer fonctionne-t-il sur une UIWebView?

J'essaie d'obtenir un UIGestureRecognizer fonctionnant avec un UIWebview qui est une sous-vue d'un UIScrollView. Cela semble étrange mais lorsque j'ai le numberOfTouchesRequired réglé sur 2, le sélecteur se déclenche, mais lorsque numberOfTouchesRequired est réglé sur 1, le sélecteur ne se déclenche pas.

Voici mon code:

UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(select1:)];
    tap1.numberOfTouchesRequired = 2;
    tap1.numberOfTapsRequired = 1;
    tap1.delegate = self;
    [self.ans1WebView addGestureRecognizer:tap1];
    [tap1 release];

- (void) select1:(UILongPressGestureRecognizer *)sender {
    //Do Stuff
}

Je l'ai confirmé en utilisant l'exemple Apple Apple pour UIGestureRecognizer et en insérant une vue Web dans leur plume. Leur code de touche fonctionne partout mais à l'intérieur de la zone de la vue Web.

40
rscott

D'après ce que j'ai vu, UIWebView ne joue pas bien avec les autres. Pour les reconnaisseurs de gestes, vous pouvez essayer de retourner OUI depuis:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

Je devais être rétrocompatible avec 3.0, donc j'ai fini par faire toute la manipulation des gestes avec Javascript dans UIWebView. Pas une bonne solution, mais cela a fonctionné pour mes besoins. J'ai pu capturer une pression longue sur un élément, ainsi que tapoter, glisser, pincer et faire pivoter. Bien sûr, j'utilisais uniquement du contenu local.

Éditer:

Vous pouvez regarder dans PhoneGap pour un exemple d'envoi de messages au délégué de l'UIWebView. En bref, vous utilisez document.location = "myscheme:mycommand?myarguments" Puis analysez la commande et les arguments de ce rappel délégué:

-(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType {
  if ( [[[inRequest URL] absoluteString] hasPrefix:@"myscheme:"] ) {
    //.. parse arguments
    return NO;
  }
}

Vous pouvez voir dans le code PhoneGap qu'ils ont mis en place une file d'attente et une minuterie pour envoyer les messages. Selon votre utilisation, vous devrez peut-être faire la même chose. Il y a d'autres questions sur SO sur ce sujet.

Voici la documentation Event Handling . Dans mon cas, j'ai ajouté des écouteurs d'événements à document à partir de <body onload="myloader();">

function myloader() {
    document.addEventListener( 'touchcancel' , touch_cancel , false );
    document.addEventListener( 'touchstart' , touch_start , false );
    document.addEventListener( 'touchmove' , touch_move , false );
    document.addEventListener( 'touchend' , touch_end , false );
};

La gestion réelle des événements dépend beaucoup de vos besoins. Chaque gestionnaire d'événements recevra un TouchEvent avec une propriété touches où chaque élément est un Touch . Vous pouvez enregistrer l'heure et le lieu de début dans votre gestionnaire de démarrage tactile. Si les touches se déplacent trop loin ou si le mauvais temps passe, ce n'est pas une touche longue.

WebKit peut essayer de gérer une touche longue pour démarrer une sélection et une copie. Dans votre gestionnaire d'événements, vous pouvez utiliser event.preventDefault(); pour arrêter le comportement par défaut. J'ai également trouvé la propriété -webkit-user-select:none Css pratique pour certaines choses.

var touch = {};
function touch_start( event /*TouchEvent*/ {
  event.preventDefault();
  touch = {};
  touch.startX = event.touches.item(0).pageX;
  touch.startY = event.touches.item(0).pageY;
  touch.startT = ( new Date() ).getTime();
}

Ce n'est que le deuxième projet avec lequel j'ai utilisé javascript. Vous pouvez trouver de meilleurs exemples ailleurs.

Comme vous pouvez le voir, ce n'est pas une réponse rapide à votre problème. Je suis assez content des résultats que j'ai obtenus. Le HTML avec lequel je travaillais n'avait aucun élément interactif au-delà d'un lien ou deux.

67
drawnonward

UIWebView a ses propres vues privées, qui ont également des reconnaisseurs de gestes attachés. Par conséquent, les règles de priorité empêchent tout identificateur de mouvement ajouté à une UIWebView de fonctionner correctement.

Une option consiste à implémenter le protocole UIGestureRecognizerDelegate et à implémenter la méthode gestureRecognizer: shouldRecognizeSimultaneousWithGestureRecognizer. Renvoyez OUI à partir de cette méthode pour d'autres gestes de prise. De cette façon, votre gestionnaire d'appels sera appelé et la vue Web sera toujours appelée.

Voir ceci sur les Apple .

45
Don Wilson

Il s'agit d'une solution quelque peu hack-ish, mais cela fonctionne. UIWebView possède de nombreux reconnaisseurs de gestes internes auxquels vous ne pouvez normalement pas accéder sans utiliser d'API privée. Vous les obtenez tous gratuitement, cependant, lorsque vous implémentez gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. À ce stade, vous pouvez dire à tous les UITapGestureRecognizers internes de ne se déclencher que lorsque votre propre UITapGestureRecognizer a échoué. Vous ne faites cela qu'une seule fois, puis supprimez le délégué de votre propre reconnaissance de gestes. Le résultat est que vous pouvez toujours effectuer un panoramique et faire défiler votre WebView, mais il ne recevra plus de gestes (simples) de toucher.

- (void)viewDidLoad 
{      
    [super viewDidLoad];

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];

    // view is your UIViewController's view that contains your UIWebView as a subview
    [self.view addGestureRecognizer:tap];

    tap.delegate = self;
}

- (void)handleTapGesture:(UITapGestureRecognizer *)gestureRecognizer
{
    NSLog(@"tap!");

    // this is called after gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: so we can safely remove the delegate here
    if (gestureRecognizer.delegate) {
        NSLog(@"Removing delegate...");
        gestureRecognizer.delegate = nil;
    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        [otherGestureRecognizer requireGestureRecognizerToFail:gestureRecognizer];

        NSLog(@"added failure requirement to: %@", otherGestureRecognizer);
    }

    return YES;
}

Prendre plaisir!

13
Johannes Fahrenkrug

J'avais le même problème. Cette solution a fonctionné pour moi:

1) Assurez-vous d'ajouter le protocole à votre interface: UIGestureRecognizerDelegate par exemple:

@interface ViewController : UIViewController <SlideViewProtocol, UIGestureRecognizerDelegate>

2) Ajoutez cette ligne de code

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES
}

3) Configuration du geste

UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(nextSlide)];
swipeGesture.numberOfTouchesRequired = 1;
swipeGesture.direction = (UISwipeGestureRecognizerDirectionLeft);
swipeGesture.cancelsTouchesInView = YES; [swipeGesture setDelegate:self];
[self.view addGestureRecognizer:swipeGesture];
4
chrisallick
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
    return YES;
}
3
Bhoopi

Une autre façon consiste à placer un bouton UIB transparent sur UIWebView et à gérer les pressions à partir de ce bouton. Dans certains cas, cela pourrait être une bonne solution.

UIWebView webView = [UIWebView new];
//...

UIButton *transparentButton = [UIButton buttonWithType:UIButtonTypeCustom];
[transparentButton addTarget:self action:@selector(buttonTapped) forControlEvents:(UIControlEventTouchUpInside)];
transparentButton.frame = webView.frame;
transparentButton.layer.backgroundColor = [[UIColor clearColor] CGColor];
[self.view transparentButton];
2
Sergey

Vous pouvez également trouver ma réponse ici utile. Il s'agit d'un exemple pratique de gestion du geste de pincement dans le javascript d'UIWebView.

(Vous pouvez communiquer les événements à l'Objective-C si vous le souhaitez. Voir ma réponse ici .)

1
zekel