web-dev-qa-db-fra.com

Comment voler des touches de UIScrollView?

Aujourd'hui, au cours de ma période de création, j'ai effectué des recherches assez complètes sur la manière de voler des contacts d'un UIScrollView et de les envoyer instantanément vers une sous-vue spécifique, tout en conservant le comportement par défaut du reste de la vue de défilement. Envisagez d'avoir un UIPickerView à l'intérieur d'un UITableView. Le comportement par défaut est que si vous faites glisser votre doigt sur la vue Sélecteur, la vue Défilement défilera et la vue Sélecteur restera inchangée.

La première chose que j'ai essayée a été de passer outre

- (BOOL)touchesShouldCancelInContentView:(UIView *)view

et tout simplement ne pas autoriser UIScrollView à annuler des effleurements dans la vue du sélecteur. Cela fonctionne, mais cela a un effet secondaire désagréable. Vous souhaitez que la vue Sélecteur réponde immédiatement et que vous deviez définir delaysContentTouches sur NO. Le problème est que vous ne voulez pas que le reste de la vue tableau réponde immédiatement, car s'il le fait, la cellule de la vue tableau sera toujours mise en surbrillance pendant quelques millisecondes avant le début du défilement.

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

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

parce que j'avais lu que la vue de défilement se retourne toujours elle-même, de sorte qu'elle "volera" les touches de ses sous-vues et les enverra plus tard à la sous-vue si elles ne présentaient aucun intérêt pour la vue de défilement. Cependant, ce n'est plus vrai. L'implémentation par défaut de hitTest: withEvent: par UIScrollView renvoie en fait la sous-vue devant recevoir le contact. Au lieu de cela, il utilise des reconnaisseurs de geste pour intercepter les touches.

La troisième chose que j'ai tentée était donc d'implémenter mon propre identificateur de geste et de le faire échouer si le contact était en dehors de la vue du sélecteur et réussissait autrement. Ensuite, je règle tous les systèmes de reconnaissance de mouvements de la vue de défilement pour qu'ils échouent, à moins que le système de reconnaissance de gestes n'échoue à l'aide du code suivant:

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

En fait, cela vole les touches de la vue par défilement, mais la vue Sélecteur ne les reçoit jamais. Alors je me suis dit que je pourrais peut-être envoyer toutes les retouches que mon identificateur de geste reçoit à l'aide de 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électeur (ou l'une de ses sous-vues) et définit l'état approprié pour la reconnaissance des gestes, comme indiqué ci-dessus. J'ai également fait la même chose pour annulé, terminé et déplacé. Cependant, l'affichage du sélecteur ne répondait toujours pas.

J'ai aussi essayé une dernière chose avant de retourner à mon travail habituel. Pendant ma longue recherche sur Google, j'ai lu que UIScrollViews imbriqué fonctionnait comme par magie depuis la version 3.x; j'ai donc essayé de placer ma vue sélecteur dans un UIScrollView imbriqué et de définir 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 de la même manière que la vue de sélecteur, de sorte que la vue de défilement interne n'a pas reçu les contacts. Je pensais que c'était un long coup, mais c'était assez simple à mettre en œuvre, alors j'ai pensé que ça valait le coup de tenter le coup.

Ce que je sais, c’est qu’UIScrollView a un identificateur de mouvements nommé UIScrollViewDelayedTouchesBeganGestureRecognizer qui intercepte les touches tactiles et les envoie à la sous-vue appropriée après 150 (?) Ms. Je pense que je devrais être capable d'écrire un identificateur similaire qui provoque l'échec des identificateurs par défaut de la vue de défilement et au lieu de retarder les touches, les envoie immédiatement à la vue Sélecteur. Donc, si quelqu'un sait comment écrire un tel identificateur, faites-le-moi savoir et, si vous avez une autre solution au problème, n'hésitez pas à le partager.

Merci d’avoir lu toute la question et même si vous ne connaissez pas la réponse, vous pouvez quand même l’inviter à nouveau pour que la question retienne plus d’attention (de la part de quelqu'un qui peut y répondre). Merci! :)

37
Erik B

Parfois, vous devez poser la question avant de pouvoir trouver la réponse. Dan Ray avait 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 cela fonctionne bien pour moi aussi. Cependant, ce n'est pas vraiment voler des touches de la vue du défilement, donc si quelqu'un sait réellement voler des touches, ce serait génial.

Source: UIPickerView dans UITableView.tableFooterView ne reçoit pas les touches de glisser

35
Erik B

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

4
Arkadiusz Matecki

Je suis aussi en retard pour la fête, mais pour les nouveaux arrivants, si vous cherchez à ignorer complètement les balayages dans la vue par défilement, ce qui a fonctionné pour moi a été d’ajouter un dispositif de reconnaissance des gestes panoramiques à la vue que je veux, comme ce:

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