2010-05-26 13 views
40

Sto tentando di ottenere un UIGestureRecognizer che funziona con una UIWebview che è una sottoview di un UIScrollView. Sembra strano, ma quando ho il numberOfTouchesRequired impostato su 2, il selettore si attiva, ma quando numberOfTouchesRequired è impostato su uno, il selettore non viene attivato.UIGestureRecognizer funziona su UIWebView?

Ecco il mio codice:

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 
} 

Ho confermato questo utilizzando l'esempio di Apple per UIGestureRecognizer e l'inserimento di un WebView nella loro pennino. Il loro codice tap funziona ovunque ma all'interno dell'area della webview.

risposta

66

Da quello che ho visto, UIWebView non funziona bene con gli altri. Per di riconoscimento gesto, si potrebbe provare a tornare SI da:

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

dovevo essere compatibile con 3,0 così ho finito per fare tutta la movimentazione con JavaScript all'interno del UIWebView gesto. Non è una buona soluzione, ma ha funzionato per le mie esigenze. Sono stato in grado di catturare una pressione prolungata su un elemento, così come toccare, scorrere, pizzicare e ruotare. Certo, stavo usando solo contenuti locali.

Edit:

Si può guardare in PhoneGap per un esempio di invio di messaggi al delegato della UIWebView. In poche parole, si utilizza document.location = "myscheme:mycommand?myarguments" quindi analizzare il comando e gli argomenti da questo delegato di callback:

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

Si può vedere nel codice PhoneGap che hanno istituito una coda e timer per inviare i messaggi. A seconda dell'utilizzo, potrebbe essere necessario fare la stessa cosa. Ci sono altre domande su SO su questo argomento.

Questa è la documentazione Event Handling. Nel mio caso ho aggiunto listener di eventi per document da <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); 
}; 

Il trattamento vero e proprio evento dipende molto dalle vostre esigenze. Ogni gestore di eventi riceverà un TouchEvent con una proprietà touches in cui ogni elemento è un Touch. È possibile registrare l'ora di inizio e la posizione nel gestore del touchstart. Se i tocchi si spostano troppo lontano o il tempo non passa, non è un tocco lungo.

WebKit può provare a gestire un tocco lungo per avviare una selezione e copiare. Nel gestore eventi è possibile utilizzare event.preventDefault(); per interrompere il comportamento predefinito. Ho anche trovato utile la proprietà css -webkit-user-select:none per alcune cose.

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

Questo è solo il secondo progetto con cui javascript è stato utilizzato. Puoi trovare esempi migliori altrove.

Come potete vedere questa non è una risposta rapida al vostro problema. Sono abbastanza soddisfatto dei risultati ottenuti. L'html con cui stavo lavorando non aveva elementi interattivi al di là di un link o due.

+3

Grazie, non so nulla di Javascript; saresti così gentile da indicarmi la giusta direzione su come rilevare un tocco in una UIWebView tramite Javascript e chiamare un metodo Objective-C? Non mi dispiacerebbe essere compatibile con 3.0 per il targeting non iPad. Per gli altri che si occupano solo di 3.2+, ho risolto il problema creando semplicemente una vista trasparente direttamente su UIWebView e rilevando i relativi tocchi. – rscott

+0

Grazie dat lavorato – user578386

+0

Grazie, funziona bene –

1

È anche possibile trovare la mia risposta here utile. È un esempio funzionante di gestione del gesto di pizzicamento nel javascript di UIWebView.

(È possibile comunicare gli eventi all'Obiettivo-C se si desidera. Vedere my answer here.)

44

UIWebView ha le proprie viste private, che dispongono anche di riconoscitori di gesti. Pertanto, le regole di precedenza impediscono il corretto funzionamento di eventuali riconoscimenti di gesti aggiunti a UIWebView.

Un'opzione consiste nell'implementare il protocollo UIGestureRecognizerDelegate e implementare il metodo gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer. Ritorna SÌ da questo metodo per altri gesti di tocco. In questo modo verrà chiamato il gestore del tocco e la visualizzazione Web verrà comunque richiamata.

Vedere this on the Apple dev forums.

+1

Vedere la 2 ° risposta in questione riservata alle informazioni supplementari: http://stackoverflow.com/questions/2627934/simultaneous-gesture-recognizers-in-iphone-sdk – ChrisP

+0

Auguro Potrei sopravviverlo più di una volta. –

+0

Questo è estremamente utile, grazie! –

4

Avevo lo stesso problema. Questa soluzione ha funzionato per me:

1) Assicurarsi di aggiungere il protocollo per l'interfaccia: UIGestureRecognizerDelegate ad esempio:

@interface ViewController : UIViewController <SlideViewProtocol, UIGestureRecognizerDelegate> 

2) Aggiungere questa riga di codice

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

3) Configurazione gesture

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]; 
+1

questa risposta deve essere modificata per restituire il codice corretto nel passaggio n. 2 (ad esempio, l'API richiede che venga restituito un valore BOOL). –

+0

Questa è la più semplice delle soluzioni presentate e funziona per me, con due avvertimenti. Devi restituire SÌ. E devi impostare swipeGesture.delegate = self; – JScarry

+0

Ho modificato il post per riflettere i suggerimenti. – chrisallick

2

Un altro modo è mettere un UIButton trasparente su UIWebView e gestire i tap da questo pulsante. In alcuni casi potrebbe essere una buona soluzione.

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]; 
+0

Hahah, questa è sicuramente la soluzione migliore :) +1 per l'hack. – chrisallick

+3

Io uso questo metodo se non ho bisogno di scorrere l'UIWebView. Ma se è necessario scorrere, non funziona poiché UIButton intercetta tutti gli input dell'utente. – JScarry

13

Questa è una soluzione un po 'hack-ish, ma funziona. UIWebView ha molti riconoscitori di gesti interni a cui normalmente non si può accedere senza utilizzare API private. Puoi averli tutti gratuitamente, però, quando si implementa gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. A quel punto, puoi dire a tutti gli UITapGestureRecognizer interni di sparare solo quando il tuo UITapGestureRecognizer ha fallito. Lo fai solo una volta e poi rimuovi il delegato dal tuo riconoscitore di gesti. Il risultato è che puoi ancora scorrere e scorrere la tua webView, ma non riceverà più alcun (singolo) tocco.

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

Divertiti!

3
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { 
    return YES; 
} 
Problemi correlati