39 votes

Comment voler les touches de UIScrollView ?

Aujourd'hui, pendant mon temps libre, j'ai fait des recherches assez complètes sur la façon de voler les touches d'une UIScrollView et de les envoyer instantanément à une sous-vue spécifique, tout en conservant le comportement par défaut pour le reste de la vue défilante. Considérons le cas d'une UIPickerView à l'intérieur d'une UITableView. Le comportement par défaut est que si vous faites glisser votre doigt sur la vue du sélecteur, la vue défilante défilera et la vue du sélecteur restera inchangée.

La première chose que j'ai essayée, c'est de remplacer

- (BOOL)touchesShouldCancelInContentView:(UIView *)view

et simplement ne pas permettre à l'UIScrollView d'annuler les touches à l'intérieur de la vue du sélecteur. Cette méthode fonctionne, mais elle a un effet secondaire désagréable. Vous aimeriez que la vue du sélecteur réagisse immédiatement, et vous devrez donc définir le paramètre delaysContentTouches à NO. Le problème est que vous ne voulez pas que le reste de la vue tableau réponde immédiatement, car si c'est le cas, la cellule de la vue tableau sera toujours mise en évidence pendant quelques millisecondes avant que le défilement ne commence.

La deuxième chose que j'ai essayée est de remplacer

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

parce que j'avais lu que la vue défilante se renvoie toujours elle-même, de sorte qu'elle "vole" les touches de ses sous-vues et les envoie plus tard à la sous-vue si elles n'étaient pas intéressantes pour la vue défilante. Cependant, ce n'est plus vrai. L'implémentation par défaut de hitTest:withEvent : de UIScrollView renvoie en fait la sous-vue qui devrait recevoir le toucher. Au lieu de cela, elle utilise des reconnaisseurs de gestes pour intercepter les touchers.

La troisième chose que j'ai tentée a donc été d'implémenter ma propre reconnaissance de gestes et de faire en sorte qu'elle échoue si le toucher est en dehors de la vue du sélecteur et qu'elle réussisse sinon. Ensuite, j'ai configuré tous les dispositifs de reconnaissance des gestes de la vue défilante pour qu'ils échouent si mon dispositif de reconnaissance des gestes échoue en utilisant le code suivant :

for (UIGestureRecognizer * gestureRecognizer in self.tableView.gestureRecognizers)
{
    [gestureRecognizer requireGestureRecognizerToFail:myRecognizer];
}

Cela permet en fait de voler les touches de la vue de défilement, mais la vue du sélecteur ne les reçoit jamais. J'ai donc pensé que je pourrais peut-être envoyer tous les touchers que ma reconnaissance de gestes reçoit en utilisant ce code :

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches)
        [touch.view touchesBegan:touches withEvent:event];
}

Le code ci-dessus est une version simplifiée. Je m'assure également que la vue est une vue de sélection (ou une de ses sous-vues) et je définis l'état approprié pour la reconnaissance des gestes comme je l'ai mentionné ci-dessus. J'ai également fait la même chose pour annulé, terminé et déplacé. Cependant, la vue du sélecteur ne répondait toujours pas.

J'ai également essayé une dernière chose avant de retourner à mon travail habituel. Lors de mes recherches approfondies sur Internet, j'ai lu que les UIScrollViews imbriquées fonctionnaient comme par magie depuis la version 3.x. J'ai donc essayé de placer ma vue de sélection dans une UIScrollView imbriquée et de lui attribuer les propriétés suivantes :

scrollView.delaysContentTouches = NO;
scrollView.canCancelContentTouches = NO;

Comme on pouvait s'y attendre, la vue de défilement externe n'a pas traité la vue de défilement interne différemment de la vue du sélecteur, de sorte que la vue de défilement interne n'a pas reçu les touches. J'ai pensé que c'était un pari risqué, mais il était assez simple à mettre en œuvre, alors j'ai pensé qu'il valait la peine d'essayer.

Ce que je sais, c'est que UIScrollView a une reconnaissance de gestes nommée UIScrollViewDelayedTouchesBeganGestureRecognizer qui intercepte les touches et les envoie à la sous-vue appropriée après 150 ( ?) ms. Je pense que je devrais être capable d'écrire une reconnaissance similaire qui fait échouer les reconnaissances par défaut de la vue défilante et qui, au lieu de retarder les touchers, les envoie immédiatement à la vue du sélecteur. Donc si quelqu'un sait comment écrire un tel recognizer, merci de me le faire savoir et si vous avez une autre solution au problème, vous êtes les bienvenus pour la partager également.

Merci d'avoir lu l'ensemble de la question et même si vous ne connaissez pas la réponse, vous pouvez toujours voter en faveur de la question afin qu'elle reçoive plus d'attention (en espérant que quelqu'un puisse y répondre). Merci ! :)

36voto

Erik B Points 12343

Parfois, il faut poser la question avant de trouver la réponse. Dan Ray a eu un problème similaire et l'a résolu avec une solution très différente.

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

    if ([result.superview isKindOfClass:[UIPickerView class]])
    {
        self.scrollEnabled = NO;
    }
    else 
    {
        self.scrollEnabled = YES;    
    }
    return result;
}

J'ai testé le code et il fonctionne bien pour moi aussi. Cependant, ce n'est pas vraiment le vol des touches de la vue défilante, donc si quelqu'un sait comment voler des touches, ce serait génial.

Source : UIPickerView à l'intérieur de UITableView.tableFooterView ne reçoit pas les contacts par glisser-déposer.

0 votes

Puisque c'est encore la seule réponse, je l'accepte comme réponse, mais si vous avez une meilleure solution, n'hésitez pas à la partager !

0 votes

C'est la solution que je préfère. Elle est simple et efficace et fonctionne tout simplement. Merci beaucoup !

1 votes

Excellent, pour mes besoins je l'ai changé en ([result isKindOfClass:[UITableView class]] || [result.superview isKindOfClass:[UITableView class]] || [result.superview isKindOfClass:[UITableViewCell class]])

4voto

Un peu tard, mais j'ai trouvé cette solution : http://www.cocoanetics.com/2010/06/hacking-uiscrollview-gesture-recognizers/ Cela fonctionne pour moi

4 votes

Il est préférable d'inclure la partie pertinente de l'article dans votre réponse plutôt qu'un lien direct.

1voto

Brian Sachetta Points 2243

Je suis également en retard sur la fête, mais pour les nouveaux venus, si vous cherchez simplement à ignorer les balayages sur la vue de défilement, ce qui a fonctionné pour moi a été d'ajouter une reconnaissance de geste de panoramique à la vue que je veux ignorer les balayages, comme ceci :

let panGesture = UIPanGestureRecognizer()
panGesture.cancelsTouchesInView = false
myView?.addGestureRecognizer(panGesture)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X