47 votes

Tutoriel sur la façon de glisser et déposer un élément d'un UITableView à un UITableView

Ça fait un moment que je me casse la tête sur ce sujet et j'ai trouvé la solution. Je veux rendre la pareille à la communauté puisque j'ai reçu beaucoup d'aide de ce site :).

J'essaie de copier un élément d'un UITableView vers un autre UITableView et les informations que j'ai trouvées sur le Web concernant la manière de procéder sont, au mieux, sommaires. Je me suis débrouillé tout seul et je vais donc décrire ma petite architecture.

  • Maître UIView
    • UIView avec UITableView
      • UITableViewCell personnalisé
        • UIView personnalisé qui est copié (objet Personne dans mon cas)
    • UIView avec UITableView
      • UITableViewCell personnalisé
        • UIView personnalisé qui est copié (objet Personne dans mon cas)

L'objet "personne" que j'ai dans l'UITableView est l'objet que je veux faire glisser et déposer d'une table vers une autre. J'ai eu beaucoup de mal à trouver comment faire sortir l'objet de la table et le faire glisser dans une autre table en un seul mouvement fluide. Pendant longtemps, il me fallait deux manipulations pour effectuer l'opération.

En commençant par l'objet Personne, il s'agit d'un objet simple qui contient une image. J'ai dû implémenter ma propre méthode touchesMoved pour modifier la position centrale de la personne lors d'un déplacement.

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    if( m_moveable == YES ){
        UITouch *touch = [touches anyObject];
        CGPoint location = [touch locationInView:self.superview];

        if( 0 < location.x-50 && location.x+50 < 768 ){ 
            if( 0 < location.y-50 && location.y+150 < 1004 ){
                self.center = location;
            }
        }
    }
}

J'ai défini l'indicateur userInteractionEnabled de l'objet Personne sur NO lors de l'initialisation afin que les clics dans la table ne soient pas pris en compte par l'objet Personne. Dans ce cas, l'objet Personne se déplacerait dans la table, ce qui va à l'encontre du but recherché.

L'objet suivant est mon UITableViewCell personnalisé. Cet objet est chargé d'attraper le premier contact de l'utilisateur. Ce qu'il est censé faire, c'est attraper ce contact et faire sortir la personne. La personne est l'une des sous-vues appartenant à l'UITableViewCell personnalisée.

 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UIView *parent = self.superview.superview.superview;    

    Person *s = nil;
    for( UIView *child in self.subviews ){
        if( [child isKindOfClass:[Person class]] ){
            s = child;
            s removeFromSuperview];
            break;
        }        
    }

    if( s != nil ){
        self.userInteractionEnabled = NO;
        s.userInteractionEnabled = YES;
        UITableView *subParent = self.superview;   //EDIT #1
        subParent.scrollEnabled = NO;              //EDIT #1

        [parent addSubview:s];
        //[self touchesEnded:touches withEvent:event]; //EDIT #1
    }
}

Il est important de noter que le drapeau userInteractionEnabled est inversé dans la méthode ci-dessus. Avant le toucher, l'objet Personne est "hors limites" pour le toucher d'une personne. Après que la cellule personnalisée ait capté un mouvement, la Personne est libérée en l'ajoutant à la vue du parent, puis activée (userInteractionEnabled=YES). L'objet Personne est alors "né" et peut gérer les contacts de déplacement de manière autonome.

Il y a un petit problème : l'objet Personne clignote dans le coin supérieur gauche, mais descend immédiatement vers le doigt de l'utilisateur.

La dernière partie de cette conception est que le UIView maître doit gérer une "transition tactile". Lorsque l'utilisateur touche la table et que l'objet Personne apparaît, l'application doit comprendre que le focus doit être retiré de la table et dirigé vers l'objet Personne. Pour ce faire, la méthode hitTest de l'UIView maître a été surchargée de la manière suivante.

- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *rv = nil;
    for(UIView *child in self.subviews){
        if( [child isKindOfClass:[Person class]] ){
            rv = child;
            child.center = point;
            break;
        }
    }
    if( rv == nil ){
        rv = [super hitTest:point withEvent:event];
    }   
    return rv;
}

La façon dont ce code fonctionne est la suivante : lorsque la personne est sortie de la table, elle n'est pas visée par une touche. La touche est "possédée" par l'UITableView à partir de laquelle la personne est sortie. La méthode hitTest est la clé pour recentrer cette touche. Régulièrement, le système vérifie quelle UIView est le point de mire d'une touche. La méthode hitTest est appelée par le système pour identifier cette UIView. Lorsque la personne est attachée à la vue principale, cette fonction hitTest parcourt toutes les vues secondaires, détecte la présence de la personne et la renvoie comme l'objet touché "dominant". Tout mouvement de votre doigt sera immédiatement signalé à la Personne et non à la UITableView.

C'est le cœur de la mise en œuvre. Pour qu'un UITableView "attrape" l'objet en mouvement, c'est simple maintenant et je vous laisse l'essayer ! Si vous avez des questions, n'hésitez pas à les poser !

EDIT #1 Déplacer l'objet Personne s'avère plus difficile que je ne le pensais :). J'ai dû ajouter une ligne pour empêcher l'UITableView de défiler lorsque le parent est déplacé parce que l'UITableView absorbe tous les événements de mouvement.
La fonction touchesEnded se déclenche dans la classe UITableViewCell personnalisée.
mj

7voto

rano Points 3094

Bonjour, j'ai réussi à créer un drag&drop d'une ligne dans une UITableView sur une autre ligne de la même table. J'ai créé un uiImageView représentant l'élément mobile (qui n'a pas été retiré de la table) et je l'ai attaché au UIView le plus en avant pour le rendre glissant sur tout l'écran. Voici quelques posts sur les difficultés que j'ai rencontrées :

  1. UITableView : Faire glisser une ligne sur une autre
  2. UITableView : défilement programmatique de la vue du contenu
  3. UITableView : les gestes personnalisés ne permettent plus le défilement

J'espère que cela pourra vous aider ^^

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