[Obj-C] Combiner UISwipeGestureRecognizer et UIPanGestureRecognizer

Ben77650Ben77650 Membre
janvier 2016 modifié dans API UIKit #1

Bonjour,


 


J'ai bien avancé sur mon projet test (qui n'utilise toujours pas l'autolayout mais que vous trouverez en pièce jointe), mais il me reste 2 points qui me bloquent.


  • Actuellement le UIPanGestureRecognizer ne reconnais pas le sens du swipe (à  savoir que je peut swiper vers le bas pour remonter, ce qui vous le concéderez n'est pas logique)
  • J'aimerais que lors de mon swipe le bouton avec la flèche (le composant expandButton) bouge en "temps réel", à  savoir que si mon doigt (ou mon curseur dans le cadre du Simulateur) à  swiper de 10 px, le bouton doit avoir bougé de 10 px (en "temps réel").

Je n'ai rien vu d'intéressant / d'utile m'aidant à  résoudre ces soucis.


 


Pouvez-vous m'aider à  régler ces problèmes s'il vous plait ?


 


Merci d'avance :)


Réponses

  • DrakenDraken Membre
    janvier 2016 modifié #2

    Dans le MOOC de Jussieu sur la programmation iOS, j'avais utilisé UIPanGestureRecognizer pour détecter le sens du mouvement en temps réel. Je te donne un fragment de mon code source. Il te sera peut-être utile, même si c'est du Swift ! 



    func panDetection(sender:UIPanGestureRecognizer) {

    // Initialisation détection
    if sender.state == .Began {
    self.choixCourant = .Inconnu
    }

    if sender.state == .Changed {
    let vitesse = sender.velocityInView(self)
    // Détermination de la direction
    if self.choixCourant == .Inconnu {
    if vitesse.x > 0 {
    self.choixCourant = .Oui
    labelGesture.text = "Oui"
    labelGesture.hidden = false
    } else {
    self.choixCourant = .Non
    labelGesture.text = "Non"
    labelGesture.hidden = false
    }
    }
    }

    // Fin de la gesture
    if sender.state == .Ended {
    // On cache le label
    labelGesture.hidden = true
    // Le controleur est prévenu du choix avec une notification
    if self.choixCourant != .Inconnu {
    self.notifierChoix(self.choixCourant)
    }

    }
    }

    La direction est calculée avec la vitesse de déplacement sur l'axe x.
    vitesse.x > 0 => droite
    vitesse.x < 0 => gauche

  • Merci Draken je vais essayer ça dans l'immédiat, je te tiens au courant


  • Ben77650Ben77650 Membre
    janvier 2016 modifié #4


     


    Dans le MOOC de Jussieu sur la programmation iOS, j'avais utilisé UIPanGestureRecognizer pour détecter le sens du mouvement en temps réel. Je te donne un fragment de mon code source. Il te sera peut-être utile, même si c'est du Swift ! 



    func panDetection(sender:UIPanGestureRecognizer) {

    // Initialisation détection
    if sender.state == .Began {
    self.choixCourant = .Inconnu
    }

    if sender.state == .Changed {
    let vitesse = sender.velocityInView(self)
    // Détermination de la direction
    if self.choixCourant == .Inconnu {
    if vitesse.x > 0 {
    self.choixCourant = .Oui
    labelGesture.text = "Oui"
    labelGesture.hidden = false
    } else {
    self.choixCourant = .Non
    labelGesture.text = "Non"
    labelGesture.hidden = false
    }
    }
    }

    // Fin de la gesture
    if sender.state == .Ended {
    // On cache le label
    labelGesture.hidden = true
    // Le controleur est prévenu du choix avec une notification
    if self.choixCourant != .Inconnu {
    self.notifierChoix(self.choixCourant)
    }

    }
    }

    La direction est calculée avec la vitesse de déplacement sur l'axe x.
    vitesse.x > 0 => droite
    vitesse.x < 0 => gauche



     


    Ca marche effectivement à  la perfection, merci à  toi ;)


     


    Maintenant il me reste ce problème à  résoudre:


    - J'aimerais que lors de mon swipe le bouton avec la flèche (le composant expandButton) bouge en "temps réel", à  savoir que si mon doigt (ou mon curseur dans le cadre du Simulateur) à  swiper de 10 px, le bouton doit avoir bougé de 10 px (en "temps réel").


  • Est-ce que tu as essayé de faire quelque chose ?


  • Effectivement.


     


    J'ai essayé de faire ça



    if (sender.state == UIGestureRecognizerStateEnded) {
    NSLog(@Vitesse.Y = %f, vitesse.y);

    if(vitesse.y > 0) {
    if(vitesse.y >= heightTobeExpanded) {
    [self changeImageButton];
    }

    }
    else {
    if(vitesse.y <= -heightTobeExpanded) {
    [self changeImageButton];
    }

    }
    }

    Mais il s'avère que ça n'a pas le comportement attendu


  • LarmeLarme Membre
    janvier 2016 modifié #7

    Euh, velocity, c'est une vitesse (en points/secondes), pas une distance.


     


    Il faudrait qu'au UIGestureRecognizerState, égal à  UIGestureRecognizerStateBegan, tu gardes le point, et qu'ensuite, au Ended/Changed, tu regardes la distance parcourue, et agir en fonction.


  • Arf, et donc pour la distance ? C'est ce que j'avais fait à  l'origine ?




  • Arf, et donc pour la distance ? C'est ce que j'avais fait à  l'origine ?




    Je ne sais pas. Je n'ai pas téléchargé ton projet.

  • J'avais fait ça



    - (void)panGesture:(UIPanGestureRecognizer *)sender {

    if (sender.state == UIGestureRecognizerStateBegan) {
    startLocation = [sender locationInView:self.view];
    }
    else if (sender.state == UIGestureRecognizerStateEnded) {
    CGPoint stopLocation = [sender locationInView:self.view];
    CGFloat dx = stopLocation.x - startLocation.x;
    CGFloat dy = stopLocation.y - startLocation.y;
    distance = sqrt(dx*dx + dy*dy );
    NSLog(@Distance: %f, distance);

    if(distance >= heightTobeExpanded)
    {
    [self extandView];
    }
    }
    }
  • ça se rapproche. Mais tu es intéressée par la distance réellement ou par la hauteur ?


    Car si je fais un long swipe sur la gauche, mais un peu vers le haut, tu vas expand la vue.


  • Ben77650Ben77650 Membre
    janvier 2016 modifié #12


    ça se rapproche. Mais tu es intéressée par la distance réellement ou par la hauteur ?


    Car si je fais un long swipe sur la gauche, mais un peu vers le haut, tu vas expand la vue.




    Uniquement par la hauteur.


     


    Donc si je comprends bien ton raisonnement je dois modifier et mettre ça, c'est bien ça ?



    if (sender.state == UIGestureRecognizerStateEnded) {
    CGPoint stopLocation = [sender locationInView:self.view];
    distance = stopLocation.y - startLocation.y;
    NSLog(@Distance: %f, distance);

  • sqrt(x*x), ça revient à  valeurAbsolue(x), hein ;)


    Mais oui, c'est ce que je ferais je pense.




  • sqrt(x*x), ça revient à  valeurAbsolue(x), hein ;)




     


    Oui je m'en suis rendu compte et j'ai modifié entre temps ^^

  • Ca résout bien mon 1er problème, mais le 2e est toujours la, malgré mes essais :/




  • Ca résout bien mon 1er problème, mais le 2e est toujours la, malgré mes essais :/




    Et tu passes en paramètre la hauteur de l'extension.

  • Je vais essayer ça, je te tiens au courant, merci en tout cas :)




  • Et tu passes en paramètre la hauteur de l'extension.




     


     


    Bon j'ai suivi ton conseil et ça donne cela



    - (void)panGesture:(UIPanGestureRecognizer *)sender {

    if (sender.state == UIGestureRecognizerStateBegan) {
    startLocation = [sender locationInView:self.view];
    }
    else if(sender.state == UIGestureRecognizerStateChanged)
    {
    vitesse = [sender velocityInView:self.view];

    CGPoint stopLocation = [sender locationInView:self.view];
    distance = stopLocation.y - startLocation.y;

    if(vitesse.y >0) {
    if(distance <= maxHeight && isViewExpanded == 0) {
    [self extandView:distance];
    }
    }
    else {
    if(distance >= -maxHeight && isViewExpanded == 1) {
    [self extandView:-distance];
    }
    }
    }

    if (sender.state == UIGestureRecognizerStateEnded) {
    CGPoint stopLocation = [sender locationInView:self.view];
    distance = stopLocation.y - startLocation.y;

    if(vitesse.y >0) {
    if(distance >= maxHeight && isViewExpanded == 1) {
    //[self extandView:distance];
    if(imgButton == 1)
    [self changeImage];
    }
    }
    else {
    if(distance <= -maxHeight && isViewExpanded == 0) {
    //[self extandView:distance];
    if(imgButton == 0)
    [self changeImage];
    }
    }
    }
    }

    - (void) extandView:(int)heightTobeExpanded
    {
    [UIView animateWithDuration:1.0
    delay:0.2
    options:UIViewAnimationOptionAllowAnimatedContent
    animations:^{
    if(isViewExpanded == 0 && heightTobeExpanded<maxHeight)
    {
    view1.frame = CGRectMake(view1.frame.origin.x, view1.frame.origin.y, view1.frame.size.width, view1.frame.size.height + heightTobeExpanded);

    expandButton.frame = CGRectMake(expandButton.frame.origin.x, expandButton.frame.origin.y + heightTobeExpanded, expandButton.frame.size.width, expandButton.frame.size.height);

    view2.frame = CGRectMake(view2.frame.origin.x, view2.frame.origin.y + heightTobeExpanded, view2.frame.size.width, view2.frame.size.height - heightTobeExpanded);

    isViewExpanded = 1;
    }
    else if(isViewExpanded == 1 && heightTobeExpanded>-maxHeight)
    {
    view1.frame = CGRectMake(view1.frame.origin.x, view1.frame.origin.y, view1.frame.size.width, view1.frame.size.height - heightTobeExpanded);

    expandButton.frame = CGRectMake(expandButton.frame.origin.x, expandButton.frame.origin.y - heightTobeExpanded, expandButton.frame.size.width, expandButton.frame.size.height );

    view2.frame = CGRectMake(view2.frame.origin.x, view2.frame.origin.y - heightTobeExpanded, view2.frame.size.width, view2.frame.size.height + heightTobeExpanded);

    isViewExpanded = 0;
    }
    }
    completion:nil];

    }

    Malheureusement le bouton ne bouge toujours pas en temps réel, en fonction de la position du doigt / curseur :(

  • Qu'est-ce qui ne marche pas exactement ? Le "temps réel" ?


    Tu as un delay de 0.2 et une durée d'animation de 1.0, donc potentiellement, l'animation ne va pas suivre en live la position du doigt, elle aura un petit délai...


  • Mais il n'aucune chance de bouger en temps réel dans ton source. Tu change son état à  la fin du mouvement (événement UIGestureRecognizerStateEnd). Si tu veux du temps réel, il faut agir pendant le mouvement (événement UIGestureRecognizerStateChanged).



  • Qu'est-ce qui ne marche pas exactement ? Le "temps réel" ?


    Tu as un delay de 0.2 et une durée d'animation de 1.0, donc potentiellement, l'animation ne va pas suivre en live la position du doigt, elle aura un petit délai...




     


    Plus que le temps réel, il s'avère qu'il ne descends pas jusque la ou il devrait descendre (à  savoir expandButton.frame.origin.y + heightTobeExpanded), mais qu'il descends beaucoup moins bas (expandButton.frame.origin.y vaut 141 alors qu'il devrait être de 235)


     




    Mais il n'aucune chance de bouger en temps réel dans ton source. Tu change son état à  la fin du mouvement (événement UIGestureRecognizerStateEnd). Si tu veux du temps réel, il faut agir pendant le mouvement (événement UIGestureRecognizerStateChanged).




     


    Et si justement je change sa position dans le Changed, dans le End, je change juste l'image ;)

  • C'est peut-être lié au fait que tu calcules une distance, et que tu ne t'attardes pas forcément à  la position de la fin du swipe...


  • Bah pourtant si



    CGPoint stopLocation = [sender locationInView:self.view];
    distance = stopLocation.y - startLocation.y;

    if(vitesse.y >0) {
    if(distance <= maxHeight && isViewExpanded == 0) {
    [self extandView:distance];
    NSLog(@jkjjj %f, expandButton.frame.origin.y );
    }
    }
    else {
    if(distance >= -maxHeight && isViewExpanded == 1) {
    [self extandView:-distance];
    }
    }


    maxHeight est la hauteur maximale (qui est égale à  115, une valeur claquée en dur pour l'instant).


     


    Et en gros je lui dis : "si la distance est inférieure ou égale à  115, bouge moi la flèche"




  • Et si justement je change sa position dans le Changed, dans le End, je change juste l'image ;)




     


    La vache, je n'arrive même plus à  lire correctement de l'Objective-C. Swift power !

  • Aucun problème Draken


     


    J'ai essayé ça mais il s'avère que ça n'a pas non plus le résultat escompté (il y a effectivement un temps réel, mais le bouton part très vite en dehors des limites de l'écran)



    vitesse = [sender velocityInView:self.view];

    CGPoint stopLocation = [sender locationInView:self.view];
    distance = stopLocation.y - startLocation.y;

    if(vitesse.y >0) {
    if(distance <= maxHeight && isViewExpanded == 0) {
    [self extandView:distance];
    if(distance == maxHeight)
    isViewExpanded = 1;
    }
    }
    else {
    if(distance >= -maxHeight && isViewExpanded == 1) {
    [self extandView:-distance];
    if(distance == -maxHeight)
    isViewExpanded = 0;
    }
    }
    }

  • Nouvelles conditions :



    if(sender.state == UIGestureRecognizerStateChanged)
    {
    vitesse = [sender velocityInView:self.view];

    CGPoint stopLocation = [sender locationInView:self.view];
    distance = stopLocation.y - startLocation.y;

    if(vitesse.y >0) {
    if(distance <= maxHeight && isViewExpanded == 0) {
    if(expandButton.frame.origin.y < 235)
    [self extandView:distance];
    else
    isViewExpanded = 1;
    }
    }
    else {
    if(distance >= -maxHeight && isViewExpanded == 1) {
    if(expandButton.frame.origin.y >120)
    [self extandView:-distance];
    else
    isViewExpanded = 0;
    }
    }


    Le souci c'est qu'il s'avère que parfois mon bouton se place en 285 ou en 35.


     


    Avez-vous une solution svp ?


  • Bon je pense avoir trouvé la solution à  mon problème (et toujours sans utiliser l'autolayout qui est la prochaine étape).


     


    Je vous met en PJ le projet.


     


    Si certains prennent la peine de le télécharger et de le regarder, je prends les feedbacks et les éventuelles améliorations :)


Connectez-vous ou Inscrivez-vous pour répondre.