2010-05-26 11 views
40

Ich versuche einen UIGestureRecognizer zu bekommen, der mit einer UIWebview arbeitet, die eine Unteransicht eines UIScrollView ist. Das hört sich komisch an, aber wenn ich die numberOfTouchesRequired auf 2 gesetzt habe, wird der Selektor ausgelöst, aber wenn numberOfTouchesRequired auf 1 gesetzt ist, wird der Selektor nicht ausgelöst.Funktioniert UIGestureRecognizer auf einem UIWebView?

Hier ist mein 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 
} 

ich dies für UIGestureRecognizer unter Verwendung der Apple-Probe bestätigt haben und eine Webansicht in ihrer Feder einsetzen. Ihr Tippcode funktioniert überall, aber im Bereich der Webansicht.

Antwort

66

Von dem, was ich gesehen habe, spielt UIWebView nicht gut mit anderen. Für Geste Erkenner, könnten Sie von JA versuchen Rückkehr:

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

ich rückwärts kompatibel sein musste mit 3,0 so landete ich tun all die Geste mit Javascript in der UIWebView Handhabung. Keine gute Lösung, aber es hat für meine Bedürfnisse funktioniert. Ich war in der Lage, ein langes Drücken auf ein Element zu erfassen sowie zu tippen, zu wischen, zu klemmen und zu drehen. Natürlich habe ich nur lokale Inhalte verwendet.

Edit:

Sie in PhoneGap für ein Beispiel sehen können Nachrichten an den Delegierten des UIWebView zu senden. Auf dem Punkt gebracht, verwenden Sie document.location = "myscheme:mycommand?myarguments" dann analysieren, den Befehl und Argumente aus diesem Delegaten Rückruf:

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

Sie können im PhoneGap-Code sehen, dass sie eine Warteschlange und Timer eingerichtet, um die Nachrichten zu senden. Abhängig von Ihrer Verwendung müssen Sie möglicherweise dasselbe tun. Es gibt andere Fragen zu SO zu diesem Thema.

Hier ist die Event Handling Dokumentation. In meinem Fall habe ich Ereignis-Listener document von <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); 
}; 

Die tatsächliche Ereignisbehandlung viel hängt von Ihren Bedürfnissen. Jeder Ereignishandler erhält eine TouchEvent mit einer Berührungseigenschaft, wobei jedes Element eine Touch ist. Sie können die Startzeit und den Standort in Ihrem Touchstart-Handler aufzeichnen. Wenn die Berührungen zu weit gehen oder die falsche Zeit verstreicht, ist es keine lange Berührung.

WebKit versucht möglicherweise, eine lange Berührung zu handhaben, um eine Auswahl zu starten und zu kopieren. In Ihrem Event-Handler können Sie event.preventDefault(); verwenden, um das Standardverhalten zu stoppen. Ich fand auch die -webkit-user-select:none CSS-Eigenschaft für einige Dinge nützlich.

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

Dies ist nur das zweite Projekt, mit dem ich Javascript verwendet habe. Sie können anderswo bessere Beispiele finden.

Wie Sie sehen, ist dies keine schnelle Antwort auf Ihr Problem. Ich bin ziemlich glücklich mit den Ergebnissen, die ich bekommen habe. Der HTML-Code, mit dem ich gearbeitet habe, hatte keine interaktiven Elemente über einen oder zwei Links hinaus.

+3

Danke, ich weiß nichts von Javascript; wärst du so freundlich, mir in die richtige Richtung zu zeigen, wie man einen Tipp in einem UIWebView über Javascript erkennt und eine Objective-C-Methode aufruft? Es würde mir nichts ausmachen, mit 3.0 kompatibel zu sein, wenn ich nicht auf iPad ziele. Für alle anderen, die nur 3,2+ interessieren, behob ich mein Problem, indem ich einfach eine transparente Ansicht direkt über dem UIWebView erstellte und die darin enthaltenen Abgriffe erkannte. – rscott

+0

Dank dat arbeitete – user578386

+0

Danke, es funktioniert gut –

1

Vielleicht finden Sie auch meine Antwort here hilfreich. Es ist ein praktisches Beispiel für die Handhabung der Pinch-Geste in UIWebView's Javascript.

(Sie könnten die Ereignisse zurück zum Objective-C kommunizieren, wenn Sie wollten. Siehe my answer here.)

44

UIWebView verfügt über eigene private Ansichten, an die auch Gestenerkenner angeschlossen sind. Daher halten Präzedenzregeln alle Gestenerkenner, die zu einem UIWebView hinzugefügt wurden, nicht richtig.

Eine Option besteht darin, das UIGestureRecognizerDelegate-Protokoll zu implementieren und die Methode GestenRecognizer zu implementieren: shallRecognizeSimultouslyWithGestureRecognizer. Geben Sie bei dieser Methode für andere Tippgesten YES zurück. Auf diese Weise rufen Sie Ihren Stepp-Handler auf, und die Web-Ansicht wird immer noch aufgerufen.

Siehe this on the Apple dev forums.

+1

die zweite Antwort in Frage Siehe für ergänzende Informationen: http://stackoverflow.com/questions/2627934/simultaneous-gesture-recognizers-in-iphone-sdk – ChrisP

+0

Ich wünsche Ich könnte das mehr als einmal aufdrängen. –

+0

Das ist sehr hilfreich, danke! –

4

Ich hatte das gleiche Problem. Diese Lösung funktionierte für mich:

1) Stellen Sie sicher, dass das Protokoll zu Ihrer Schnittstelle hinzuzufügen: UIGestureRecognizerDelegate zum Beispiel:

@interface ViewController : UIViewController <SlideViewProtocol, UIGestureRecognizerDelegate> 

2) Fügen Sie diese Codezeile

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

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

Diese Antwort sollte bearbeitet werden, um den richtigen Code in Schritt # 2 zurückzugeben (d. H., Dass für die API ein BOOL zurückgegeben werden muss). –

+0

Dies ist die einfachste der vorgestellten Lösungen und funktioniert für mich, mit zwei Vorbehalten. Sie müssen JA zurückgeben. Und du musst swipeGesture.delegate = self einstellen; – JScarry

+0

Ich habe den Beitrag bearbeitet, um die Vorschläge zu reflektieren. – chrisallick

2

Eine andere Möglichkeit besteht darin, eine transparente UIButton oben auf UIWebView zu setzen und Taps zu handhaben von diesem Knopf. In einigen Fällen könnte es eine gute Lösung sein.

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, das ist definitiv die beste Lösung :) +1 für den Hack. – chrisallick

+3

Ich benutze diese Methode, wenn ich das UIWebView nicht scrollen muss. Aber wenn Sie scrollen müssen, funktioniert es nicht, da der UIButton alle Benutzereingaben auffängt. – JScarry

13

Dies ist eine etwas hack-ish-Lösung, aber es funktioniert. UIWebView verfügt über viele interne Gestenerkennungsprogramme, auf die Sie normalerweise ohne private API nicht zugreifen können. Sie erhalten sie jedoch alle kostenlos, wenn Sie gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: implementieren. An diesem Punkt können Sie allen internen UITapGestureRecognizers mitteilen, dass sie nur ausgelöst werden, wenn Ihr eigener UITapGestureRecognizer fehlgeschlagen ist. Sie tun das nur einmal und entfernen dann den Delegaten von Ihrem eigenen Gestenerkenner. Das Ergebnis ist, dass Sie Ihr WebView zwar verschieben und scrollen können, aber keine (einzelne) Tippgesten mehr erhalten.

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

Genießen Sie!

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