2012-07-14 12 views
20

Ich möchte, dass sowohl mein UIScrollView als auch seine Unteransichten alle Berührungsereignisse innerhalb der Unteransicht empfangen. Jeder kann auf seine Art antworten.Lassen Sie zu, dass UIScrollView und seine Unteransichten auf eine Berührung antworten

Wenn die Tipp-Gesten an Unteranfragen weitergeleitet werden, wäre alles gut.

Viele Menschen kämpfen in diesem allgemeinen Bereich. Hier sind ein paar der vielen damit verbundenen Fragen:

How does UIScrollView steal touches from its subviews
How to steal touches from UIScrollView?
How to Cancel Scrolling in UIScrollView

Übrigens, wenn ich hitTest außer Kraft setzen: withevent: in der Scroll-Ansicht, ich tun, um die Noten sehen, solange userInteractionEnabled JA . Aber das löst mein Problem nicht wirklich, denn:

1) An diesem Punkt weiß ich nicht, ob es ein Hahn ist oder nicht.
2) Manchmal muss ich userInteractionEnabled auf NO setzen.

EDIT: Um zu klären, ja, ich möchte Taps anders als Pfannen behandeln. Taps sollten durch Subviews behandelt werden. In der Scroll-Ansicht können Pfannen wie gewohnt bearbeitet werden.

+4

Rahmen userInteractionEnabled auf NO und erwarten Benutzerinteraktionsdaten scheint wie ein Universum erschütternde Paradox. – borrrden

+0

Möchten Sie zwischen einer Berührung und einer Schriftrolle unterscheiden? – shannoga

Antwort

38

Erstens Haftungsausschluss verlängert inspiriert. Wenn Sie userInteractionEnabled auf NO auf der UIScrollView festlegen, werden keine Touch-Ereignisse an die Untersichten übergeben. Soweit mir bekannt ist, gibt es mit einer Ausnahme keinen Weg: Berühre Berührungsereignisse in der Superansicht des UIScrollView und übertrage diese Ereignisse speziell an die Unteransichten von UIScrollView. Um ehrlich zu sein, ich weiß nicht, warum Sie das tun wollen. Wenn Sie bestimmte UIScrollView-Funktionen deaktivieren möchten (wie ... nun, Scrollen), können Sie dies einfach tun, ohne UserInteraction zu deaktivieren.

Wenn ich Ihre Frage verstehe, müssen Sie Tap-Ereignisse von UIScrollView verarbeitet und an die Subviews übergeben? Auf jeden Fall (was auch immer die Geste ist), ich denke, was Sie suchen, ist die Protokollmethode gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: im Protokoll UIGestureRecognizerDelegate. Stellen Sie in Ihren Untersichten, unabhängig von den Gestenerkennern, einen Delegaten (vermutlich die Klasse an erster Stelle) auf dem Gestenerkenner ein. Überschreiben Sie die obige Methode und geben Sie YES zurück. Nun wird diese Geste zusammen mit allen anderen Erkennern erkannt, die die Geste "gestohlen" haben könnten (in Ihrem Fall ein Tippen). Mit dieser Methode können Sie Ihren Code sogar so einstellen, dass er nur bestimmte Gesten an die Unteransichten sendet oder die Geste nur in bestimmten Situationen sendet. Es gibt Ihnen viel Kontrolle. Nur sicher sein, um die Methode zu lesen über, vor allem in diesem Teil:

Diese Methode wird aufgerufen, wenn die Anerkennung einer Geste durch entweder GestureRecognizer oder otherGestureRecognizer die Erkennungs andere Geste Anerkennung seiner Geste blockieren würde. Beachten Sie, dass mit der Rückgabe von YES die gleichzeitige Erkennung garantiert ist; die Rückgabe von NO andererseits ist nicht garantiert, simultane Erkennung zu verhindern, da der Delegat der anderen Gestenerkenner YES zurückgeben kann.

Natürlich gibt es einen Vorbehalt: Dies gilt nur für Gestenerkenner. So können Sie immer noch Probleme haben, wenn Sie versuchen, touchesBegan:, touchesEnded usw. zu verwenden, um die Berührungen zu verarbeiten. Sie können natürlich hitTest: verwenden, um rohe Touch-Ereignisse an die Unteransichten zu senden, aber warum? Warum sollten Sie die Ereignisse unter Verwendung dieser Methoden in UIView verarbeiten, wenn Sie eine UIGestureRecognizer an eine Ansicht anhängen können und alle diese Funktionen kostenlos erhalten? Wenn Sie Berührungen in einer Weise verarbeitet haben, die kein Standard UIGestureRecognizer bieten kann, UnterklasseUIGestureRecognizer und bearbeiten Sie die Berührungen dort. Auf diese Weise erhalten Sie die gesamte Funktionalität eines UIGestureRecognizer zusammen mit Ihrer eigenen benutzerdefinierten Touch-Verarbeitung. Ich glaube wirklich, dass Apple für UIGestureRecognizer vorgesehen hat, um die meisten (wenn nicht alle) benutzerdefinierten Verarbeitungscodes zu ersetzen, die Entwickler auf UIView verwenden. Es ermöglicht die Wiederverwendung von Code und es ist viel einfacher zu handhaben, wenn es darum geht, welchen Code welcher Touch-Event verarbeitet.

+1

Super, danke! Wenn jemand anderes dies liest, können Sie möglicherweise einen ähnlichen Effekt mit der UIScrollView-Methode erreichen. TouchesShouldCancelInContentView –

+0

"Wenn Sie bestimmte UIScrollView-Funktionen deaktivieren möchten (wie ... nun, Scrollen), können Sie das einfach tun, ohne sie zu deaktivieren UserInteraction "->" myScrollView.scrollEnabled = false " – RGML

0

Wenn Sie zwischen einer Berührung und einem Bildlauf unterscheiden müssen, können Sie testen, ob Berührungen verschoben wurden. Wenn dies ein Tippen ist, wird touchHasBeenMoved nicht aufgerufen, dann können Sie annehmen, dass dies eine Berührung ist.

An diesem Punkt können Sie einen Booleschen Wert festlegen, der angibt, ob ein movnent diesen Zustand angenommen hat, und diesen Zustand als Bedingung in Ihren anderen Methoden festlegen.

Ich bin auf der Straße, aber wenn das ist, was Sie brauchen, werde ich in der Lage sein, später besser zu erklären.

3

Ich weiß nicht, ob das Ihnen helfen kann, aber ich hatte ein ähnliches Problem, wo ich wollte, dass die Scrollview mit Doppeltippen umgehen, aber einzelne Tippen auf Subviews weiterleiten. Hier ist der Code in einem CustomScrollView

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 

    UITouch* touch = [touches anyObject]; 
    // Coordinates 
    CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]]; 

    // One tap, forward 
    if(touch.tapCount == 1){ 
     // for each subview 
     for(UIView* overlayView in self.subviews){ 
      // Forward to my subclasss only 
      if([overlayView isKindOfClass:[OverlayView class]]){ 
       // translate coordinate 
       CGPoint newPoint = [touch locationInView:overlayView]; 
       //NSLog(@"%@",NSStringFromCGPoint(newPoint)); 

       BOOL isInside = [overlayView pointInside:newPoint withEvent:event]; 
       //if subview is hit 
       if(isInside){ 
        Forwarding 
        [overlayView touchesEnded:touches withEvent:event]; 
        break; 
       } 
      } 
     } 

    } 
    // double tap : handle zoom 
    else if(touch.tapCount == 2){ 

     if(self.zoomScale == self.maximumZoomScale){ 
      [self setZoomScale:[self minimumZoomScale] animated:YES]; 
     } else { 
      CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point];    

      [self zoomToRect:zoomRect animated:YES]; 
     } 

     [self setNeedsDisplay]; 

    } 
} 

Natürlich ist die effektive Code verwendet wird, geändert werden soll, aber an diesem Punkt sollten Sie alle Informationen die Sie benötigen, um zu entscheiden, ob Sie die Veranstaltung zu übermitteln haben. Möglicherweise müssen Sie dies in einer anderen Methode als touchesMoved implementieren: withEvent :.

Hoffe, das kann helfen.

0

Ein hackischer Weg, um Ihr Ziel zu erreichen - nicht 100% genau - ist Unterklasse der UIWindow und überschreiben das - (void) sendEvent: (UIEvent *) Ereignis;

Ein kurzes Beispiel:

in SecondResponderWindow.h Kopf

//SecondResponderWindow.h 

@protocol SecondResponderWindowDelegate 
- (void)userTouchBegan:(id)tapPoint onView:(UIView*)aView; 
- (void)userTouchMoved:(id)tapPoint onView:(UIView*)aView; 
- (void)userTouchEnded:(id)tapPoint onView:(UIView*)aView; 
@end 

@interface SecondResponderWindow : UIWindow 
@property (nonatomic, retain) UIView *viewToObserve; 
@property (nonatomic, assign) id <SecondResponderWindowDelegate> controllerThatObserves; 
@end 

in SecondResponderWindow.m

//SecondResponderWindow.m 

- (void)forwardTouchBegan:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchBegan:touch onView:aView]; 
} 
- (void)forwardTouchMoved:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchMoved:touch onView:aView]; 
} 
- (void)forwardTouchEnded:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchEnded:touch onView:aView]; 
} 

- (void)sendEvent:(UIEvent *)event { 
    [super sendEvent:event]; 

    if (viewToObserve == nil || controllerThatObserves == nil) return; 

    NSSet *touches = [event allTouches]; 
    UITouch *touch = [touches anyObject]; 
    if ([touch.view isDescendantOfView:viewToObserve] == NO) return; 

    CGPoint tapPoint = [touch locationInView:viewToObserve]; 
    NSValue *pointValue = [NSValue valueWithCGPoint:tapPoint]; 

    if (touch.phase == UITouchPhaseBegan) 
     [self forwardTouchBegan:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseMoved) 
     [self forwardTouchMoved:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseEnded) 
     [self forwardTouchEnded:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseCancelled) 
     [self forwardTouchEnded:pointValue onView:touch.view]; 
} 

Es ist nicht 100% entspricht, was Ihr erwartet hatten - denn Ihre zweite Responder View behandelt das Touch-Ereignis nicht nativ über -touchDidBegin: oder so und muss das SecondResponderWindowDelegate implementieren. Mit diesem Hack können Sie jedoch Berührungsereignisse bei zusätzlichen Respondern behandeln.

Diese Methode wird von und aus MITHIN Kumars TapDetectingWindow

2

Ich hatte das gleiche Problem, aber mit einer Scrollview, die innerhalb UIPageViewController war, so musste es etwas anders behandelt werden.

Durch Ändern der cancelsTouchesInView Eigenschaft auf false für jede Erkennung auf der UIScrollView konnte ich berührt, um Tasten innerhalb der UIPageViewController.

Ich tat dies, indem Sie diesen Code in viewDidLoad Zugabe:

guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else { 
    print("No gesture recognizers on scrollview.") 
    return 
} 

for recognizer in recognizers { 
    recognizer.cancelsTouchesInView = false 
}