web-dev-qa-db-fra.com

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

Je me tape la tête sur celui-ci depuis un moment et je l'ai compris. Je veux redonner à la communauté car 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 vues sur le Web concernant la façon de procéder sont au mieux fragmentaires. Je l'ai compris moi-même et je vais donc décrire ma petite architecture.

  • Master 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 UITableView est l'objet que je veux faire glisser et déposer d'une table vers une autre. J'ai eu le plus de mal à trouver comment sortir l'élément de la table et le faire glisser en un seul mouvement fluide. Pendant très longtemps, il me faudrait deux touches pour effectuer l'opération.

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

-(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 Person sur NO lors de l'initialisation afin que les clics dans la table ne soient pas interceptés par l'objet Person. L'objet Personne dans ce cas se déplacerait dans la table, ce qui va à l'encontre de l'objectif.

L'objet suivant est mon UITableViewCell personnalisé. Cet objet est responsable d'attraper la première touche de l'utilisateur. Ce qu'il est censé faire, c'est attraper cette touche et "faire sortir" la Personne. La personne est l'une des sous-vues appartenant à UITableViewCell personnalisé.

 - (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 l'indicateur userInteractionEnabled est retourné dans la méthode ci-dessus. Avant le toucher, l'objet Personne est "hors limites" au toucher d'une personne. Une fois que la cellule personnalisée a capturé 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 touches de mouvement tout seul.

Cela a un petit problème, car l'objet Personne clignote dans le coin supérieur gauche, puis tombe immédiatement au doigt de l'utilisateur.

La dernière partie de cette conception est que l'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 se rendre compte que le focus doit être supprimé de la table et dirigé vers l'objet Personne. La façon dont cela a été fait est que la méthode hitTest dans l'UIView maître a été surchargée avec ce qui suit.

- (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, c'est que lorsque la personne est sortie de la table, elle n'a pas une touche concentrée sur elle. La touche est "détenue" par l'UITableView d'où la personne a été extraite. La méthode hitTest est la clé pour recentrer ce toucher. Régulièrement, le système vérifie quelle UIView est au centre 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 sous-vues et détecte la présence de la personne et la renvoie en tant qu'objet touché "dominant". Tout mouvement de votre doigt sera immédiatement signalé à la personne et non à UITableView.

Ce sont les tripes de la mise en œuvre. Pour avoir une "capture" UITableView, l'objet en mouvement est simple maintenant et je vous laisse le soin d'essayer! Si vous avez des questions, veuillez les poster!

EDIT # 1 La suppression de l'objet Person 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 aspire tous les événements de mouvement.
La fonction touchesEnded se déclenche dans la classe UITableViewCell personnalisée.
mj

47
mj_

Salut, j'ai réussi à créer un glisser-déposer d'une ligne dans un UITableView sur une autre ligne de la même table. J'ai créé une uiImageView représentant l'élément en mouvement (qui n'a pas été supprimé du tableau) et je l'ai attaché à l'UIView le plus en avant pour le faire glisser sur tout l'écran. Voici quelques articles sur les douleurs que j'ai rencontrées:

  1. ITableView: faites glisser une ligne sur une autre
  2. ITableView: faire défiler par programme la vue du conten
  3. ITableView: les gestes personnalisés ne font plus défiler

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

7
rano