2010-11-29 8 views
12

Ich versuche jede Aktivität (d. H. Berührungen) abzufangen, die in meiner gesamten Anwendung passiert.UIView's -hitTest: withEvent: dreimal aufgerufen?

Mit anderen Worten, ich versuche, über jedes Berührungsereignis informiert zu werden, das in meiner UIView-Hauptansicht passiert und den Rest meiner Steuerelemente enthält. Um dies zu tun, dachte ich, die UIView-Methode -hitTest: withEvent: war eine gute Lösung.

Wenn ich diese überschreibende Methode jedoch vor dem Aufruf von [super hitTest: ... withEvent: ...] anmelde, sehe ich, dass es dreimal aufgerufen wird für jede Berührung, die ich mache, und ich sehe keinen Unterschied darin das Ereignis, das ich jedes Mal erhalte, wenn es angerufen wird.

Hier ist, wie wird das Verfahren in der Hauptansicht von meiner Anwendung implementiert:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    NSLog(@"hitTest:withEvent called :"); 
    NSLog(@"Event: %@", event); 
    NSLog(@"Point: %@", NSStringFromCGPoint(point)); 
    NSLog(@"Event Type: %d", event.type); 
    NSLog(@"Event SubType: %d", event.subtype); 
    NSLog(@"---"); 

    return [super hitTest:point withEvent:event]; 
} 

Und hier ist das, was ich NSLog für eine einzelne Note in dieser Ansicht:

2010-11-29 14:09:26.892 Application[68818:207] hitTest:withEvent called : 
2010-11-29 14:09:26.892 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)} 
2010-11-29 14:09:26.892 Application[68818:207] Point: {173, 498} 
2010-11-29 14:09:26.892 Application[68818:207] Event Type: 0 
2010-11-29 14:09:26.892 Application[68818:207] Event SubType: 0 
2010-11-29 14:09:26.893 Application[68818:207] --- 
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called : 
2010-11-29 14:09:26.893 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)} 
2010-11-29 14:09:26.893 Application[68818:207] Point: {173, 498} 
2010-11-29 14:09:26.893 Application[68818:207] Event Type: 0 
2010-11-29 14:09:26.893 Application[68818:207] Event SubType: 0 
2010-11-29 14:09:26.893 Application[68818:207] --- 
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called : 
2010-11-29 14:09:26.894 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37944.9 touches: {(
)} 
2010-11-29 14:09:26.894 Application[68818:207] Point: {173, 498} 
2010-11-29 14:09:26.894 Application[68818:207] Event Type: 0 
2010-11-29 14:09:26.894 Application[68818:207] Event SubType: 0 
2010-11-29 14:09:26.894 Application[68818:207] --- 

Wie könnte ich Machen Sie einen Unterschied zwischen diesen drei Benachrichtigungen, um die Aktion auszulösen, die ich nur einmal für eine einzelne Berührung ausführen möchte.

Vielen Dank im Voraus!

+0

Haben Sie jemals eine saubere Lösung gefunden? Ich habe das gleiche Problem. Die Zeitauswertung scheint zu funktionieren, könnte aber in Zukunft leicht für SDK-Änderungen anfällig sein. –

+0

Glück, warum das passiert? Ich versuche, das selbst herauszufinden – prostock

+0

Irgendwelche Fortschritte zu diesem Thema? –

Antwort

2

Die Anzahl der Ereignisantworten, die Sie erhalten, hängt von der Ansichtshierarchie ab.

Dieses Verfahren durchläuft die Ansicht Hierarchie durch Senden des pointInside: withevent: Nachricht an jeden subview zu bestimmen, die SubView sollte eine Berührungsereignis empfangen. Wenn pointInside: withEvent: gibt YES zurück, dann ist die Hierarchie der Unteransicht Traversed; Andernfalls wird der Zweig der Ansichtshierarchie ignoriert. Sie selten müssen diese Methode selbst aufrufen, aber Sie können es auf überschreiben Touch-Ereignisse von Subviews zu verbergen.

Diese Methode ignoriert Ansicht Objekte, die verborgen sind, die behinderte Benutzer Interaktion haben oder eine Alpha-Ebene weniger als 0,01. Diese Methode berücksichtigt nicht den Inhalt der Ansicht bei der Ermittlung eines Treffers. Daher kann eine Ansicht immer noch zurückgegeben werden, selbst wenn der angegebene Punkt in einem transparenten Teil des Inhalts dieser Ansicht ist.

Punkte, die außerhalb des Empfängers Grenzen liegen, werden nie als Treffer gemeldet, selbst wenn sie innerhalb eines des Empfängers Subviews liegen tatsächlich. Dies kann auftreten, wenn die aktuelle clipsToBounds-Eigenschaft der aktuellen Ansicht auf NO festgelegt ist und die betroffene Unteransicht erweitert über die Grenzen der Ansicht.

Vom UIView Class Reference.

Kurz gesagt, wenn die Ansicht, die Sie berühren, drei Unteransichten hat und diese Ansichten sichtbar sind und innerhalb der Grenzen ihrer Superview- und Touch-Region liegen, erhalten Sie drei Treffertestantworten.

+1

Nun, meine Hauptansicht enthält mehr als drei Unteransichten, also sollte ich nicht mehr als 3 Antworten erhalten? Wenn ich den gleichen Test mit einem neuen Projekt mache, das nur eine Ansicht enthält, bekomme ich genau die gleichen Ergebnisse. –

+0

Verwenden Sie den Simulator oder das tatsächliche Gerät? –

+0

Hmmm .. Ich habe nur im Simulator getestet. Ich probiere das reale Gerät aus und komme wieder zu dir –

10

Es gibt tatsächlich 3 Aufrufe zum hitTest. Es ist nicht klar, warum, aber wir können durch die Zeitstempel auf dem Ereignis vermuten, dass die ersten zwei Anrufe mit dem Abschließen der vorherigen Geste zu tun haben - diese Zeitstempel sind immer sehr nah an der vorherigen Berührung, und werden eine Entfernung von der sein aktuelle Uhrzeit.

Dies ist, wie ich die hitTest zu Prozess bestimmen:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 
    NSTimeInterval system = [[NSProcessInfo processInfo] systemUptime]; 

    if (system - event.timestamp > 0.1) { 
    // not the event we were interested in 
    } else { 
    // use this call 
    } 
} 
+1

scheint ein wenig hacky, aber funktioniert :) –

7

Wenn dies für Sie immer noch ein Problem.

Ich habe mehrere Beispiele und Diskussionen zu diesem Thema gefunden, aber die richtige Lösung ist ziemlich einfach.

Im Allgemeinen wird Hittest dreimal innerhalb einer UIView oder UIScrollView aufgerufen - dies ist das Ergebnis der Durchsicht der View-Hierarchie.

Ein ganz einfach und für mich immer die passende Lösung ist:

die Funktion hittest in dem Sie

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 

für mich umgesetzt Ansicht implementieren - alle drei Funktionsaufrufe haben immer die gleiche Position - so einfach Speichern Sie den Speicherort in Ihrer lokalen Klasse.

in der .h Datei:

@interface MyScrollView : UIScrollView { 
    CGPoint touchLocation_; 
} 

@property (nonatomic, readonly) CGPoint touchLocation; 

in der .m Datei

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ 
    return [super hitTest:point withEvent:event]; 
} 

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ 
// The point check you need  
    if(point.y != self.touchLocation.y){ 
     touchLocation_ = point; 
    // The function call you need - just called once. 
    } 
    return [super pointInside:point withEvent:event]; 
} 

Diese Lösung funktioniert für mich ganz gut in mehreren Projekten.

Verwandte Themen