2009-02-20 10 views
6

J'ai un UIPickerView qui est passé à 20% alpha lorsqu'il n'est pas utilisé. Je veux que l'utilisateur puisse toucher le sélecteur et le faire retomber.Répondre à touchBegan dans UIPickerView au lieu de UIView

Je peux le faire fonctionner si je mets une méthode touchesBegan sur la vue principale, mais cela ne fonctionne que lorsque l'utilisateur touche la vue. J'ai essayé de sous-classer UIPickerView et d'avoir un touchesBegan là-dedans, mais cela n'a pas fonctionné.

Je suppose que c'est quelque chose à voir avec la chaîne Responder, mais je n'arrive pas à m'en sortir.

Répondre

10

J'ai cherché une solution à ce problème pendant plus d'une semaine. Je vous réponds même si vous avez plus d'un an en espérant que cela aide les autres.

Désolé si ma langue n'est pas très technique, mais je suis assez nouveau dans le développement d'Objective-C et de l'iPhone.

Sous-classe UIpickerView est la meilleure façon de le faire. Mais vous devez remplacer la méthode - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event. C'est la méthode appelée chaque fois que vous touchez l'écran et elle renvoie la vue qui réagira au toucher. En d'autres termes, la vue dont la méthode touchesBegan:withEvent: sera appelée.

Le UIPickerView a 9 vues! Dans l'implémentation de la classe UIPickerView - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ne retournera pas (cela signifie que le touchesBegan:withEvent: que vous écrivez dans la sous-classe ne sera pas appelée) mais retournera une sous-vue, exactement la vue à l'index 4 (une sous-classe non documentée appelée UIPickerTable).

L'astuce est de faire la méthode - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event pour revenir self donc vous avez le contrôle sur les touchesBegan:withEvent:, touchesMoved:withEvent: et touchesEnded:withEvent: méthodes.

Dans ces méthodes, afin de conserver les fonctionnalités standard de UIPickerView, vous devez vous rappeler de les rappeler, mais dans la sous-vue UIPickerTable.

J'espère que cela a du sens. Je ne peux pas écrire de code maintenant, dès que je suis à la maison, je vais éditer cette réponse et ajouter du code.

8

Voici un code qui fait ce que vous voulez:

@interface TouchDetectionView : UIPickerView { 

} 
- (UIView *)getNextResponderView:(NSSet *)touches withEvent:(UIEvent *)event; 
@end 
@implementation TouchDetectionView 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UIView * hitTestView = [self getNextResponderView:touches withEvent:event]; 
    [hitTestView touchesBegan:touches withEvent:event]; 
} 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UIView * hitTestView = [self getNextResponderView:touches withEvent:event]; 
    [hitTestView touchesMoved:touches withEvent:event]; 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UIView * hitTestView = [self getNextResponderView:touches withEvent:event]; 
    [hitTestView touchesEnded:touches withEvent:event]; 
} 

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UIView * hitTestView = [self getNextResponderView:touches withEvent:event]; 
    [hitTestView touchesCancelled:touches withEvent:event]; 
} 

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

- (UIView *)getNextResponderView:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UITouch * touch = [touches anyObject]; 
    CGPoint point = [touch locationInView:self]; 
    UIView * hitTestView = [super hitTest:point withEvent:event]; 

    return (hitTestView == self) ? nil : hitTestView; 
} 
+0

merci :) ça a marché – ArunGJ

+1

ce code semble fonctionner, sauf si une touche se produit sur le cadre qui entoure immédiatement la roue de sélecteur. faire cela semble provoquer une boucle infinie d'appels à la méthode getNextResponderView: withEvent:. – pistachionut

+1

ya je l'ai empêché en renvoyant nil au lieu de hitTestView de getNextResponderView: withEvent: si j'ai touché la roue environnante. – ArunGJ

1

Les deux réponses ci-dessus ont été très utiles, mais j'ai un UIPickerView niché dans un UIScrollView. Je fais également un rendu continu ailleurs à l'écran lorsque l'interface graphique est présente. Le problème est que UIPickerView ne se met pas à jour complètement lorsque: une ligne non sélectionnée est tapée, le sélecteur est déplacé de sorte que deux lignes chevauchent la zone de sélection, ou une ligne est glissée mais le doigt glisse hors de l'UIPickerView. Ensuite, ce n'est que lorsque UIScrollView est déplacé que le sélecteur est mis à jour instantanément. Ce résultat est moche.

La cause du problème: mon rendu continu empêchait l'animation de UIPickerView d'obtenir les cycles de l'unité centrale nécessaires pour terminer, donc d'afficher la sélection correcte en cours. Ma solution - qui fonctionne - était la suivante: dans le touchesEnded:withEvent: de UIPickerView, exécutez quelque chose pour mettre en pause mon rendu pendant un moment. Voici le code:

#import "SubUIPickerView.h" 

@implementation SubUIPickerView 

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event 
{ 
    [pickerTable touchesBegan:touches withEvent:event]; 
} 

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event 
{ 
    [pickerTable touchesMoved:touches withEvent:event]; 
} 

- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event 
{ 
    [singleton set_secondsPauseRendering:0.5f]; // <-- my code to pause rendering 

    [pickerTable touchesEnded:touches withEvent:event]; 
} 

- (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event 
{ 
    [pickerTable touchesCancelled:touches withEvent:event]; 
} 

- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent*)event 
{ 
    if (CGRectContainsPoint(self.bounds, point)) 
    { 
     if (pickerTable == nil) 
     { 
      int nSubviews = self.subviews.count; 
      for (int i = 0; i < nSubviews; ++i) 
      { 
       UIView* view = (UIView*) [self.subviews objectAtIndex:i]; 
       if ([view isKindOfClass:NSClassFromString(@"UIPickerTable")]) 
       { 
        pickerTable = (UIPickerTable*) view; 
        break; 
       } 
      } 
     } 
     return self; // i.e., *WE* will respond to the hit and pass it to UIPickerTable, above. 
    } 
    return [super hitTest:point withEvent:event]; 
} 

@end 

et l'en-tête, SubUIPickerView.h:

@class UIPickerTable; 

@interface SubUIPickerView : UIPickerView 
{ 
    UIPickerTable* pickerTable; 
} 

@end 

Comme je l'ai dit, cela fonctionne.Le rendu se met en pause pendant une seconde supplémentaire (il s'arrête déjà lorsque vous faites glisser UIScrollView), ce qui permet à l'animation UIPickerView de se terminer. L'utilisation de NSClassFromString() signifie que vous n'utilisez aucune API non documentée. Messing avec la chaîne Responder n'était pas nécessaire. Merci à checcco et à Tylerc230 pour m'avoir aidé à trouver ma propre solution!

+0

Cela ne fonctionne plus avec UIPickerView d'iOS 8. – MatthiasC

+0

@MatthiasC savez-vous pourquoi ça ne marche plus? – stonedauwg

+0

Ces articles sur UIPickerView sont vraiment vieux. C'était nouveau et flakey à l'époque. Apple a probablement (espérons-le) complètement réimplémenté maintenant! – electromaggot

0

Set canCancelContentTouches et delaysContentTouches de vue des parents à NO, qui a travaillé pour moi