web-dev-qa-db-fra.com

Animation de UITableViewCell personnalisée en entrant en mode édition

Contexte

Tout d’abord, merci beaucoup à atebits pour leur blog très informatif Fast Scrolling in Tweetie avec UITableView . Cet article explique en détail comment les développeurs ont réussi à tirer le maximum de performances de défilement dans UITableViews dans Tweetie .

Buts

Commençant par le code source lié à la publication de blog ( original ) ( mon dépôt github ):

  1. Autoriser un UITableView utilisant ces cellules personnalisées à passer en mode édition, exposant ainsi l'interface utilisateur permettant de supprimer un élément de la table. ( github commit )

  2. Déplacez le texte de la cellule lorsque le contrôle de suppression se glisse à partir de la gauche. C'est complet, bien que le texte saute d'avant en arrière sans animation. ( github commit )

  3. Appliquez une animation au mouvement de texte de l'objectif 2 ci-dessus pour une expérience utilisateur fluide. C'est l'étape où je suis resté coincé.

La question

Quelle est la meilleure façon de présenter cette animation pour atteindre l'objectif 3? Ce serait bien si cela pouvait être fait de manière à garder la logique de mon dernier commit parce que j'aimerais beaucoup pouvoir déplacer uniquement la partie en conflit de la vue, tandis que les parties non conflictuelles (telles que -justifié) restez au même endroit ou déplacez un nombre différent de pixels. Si ce qui précède n'est pas possible, annuler ma dernière validation et la remplacer par une option permettant de faire glisser l'intégralité de la vue vers la droite constituerait également une solution viable.

J'apprécie toute aide que tout le monde peut fournir, qu'il s'agisse de pointeurs rapides, d'idées ou de fragments de code ou de commits github. Bien sûr, vous êtes le bienvenu à fork my repo si vous le souhaitez. Je resterai impliqué dans cette question pour m'assurer que toute résolution réussie est engagée dans github et documentée ici. Merci beaucoup pour votre temps!

Pensées mises à jour

J'y ai beaucoup réfléchi depuis mon premier billet et me suis rendu compte que le fait de déplacer certains éléments de texte par rapport à d'autres éléments dans la vue pourrait annuler certains des objectifs de performance d'origine résolus dans le blog. Donc, à ce stade, je pense à une solution dans laquelle la sous-vue unique entière est animée dans sa nouvelle position peut être la meilleure.

Deuxièmement, si cela est fait de cette manière, il peut arriver que la sous-vue ait une couleur personnalisée ou un arrière-plan en dégradé. Espérons que cela puisse être fait de manière à ce que l'arrière-plan soit dans sa position normale, suffisamment à gauche pour que l'arrière-plan personnalisé reste visible sur toute la cellule lorsque la vue est déplacée vers la droite.

49
Adam Alexander

Grâce à la réponse de Craig qui m'a orienté dans la bonne direction, j'ai une solution à cela. J'ai annulé mon commit qui a déplacé la position du texte en fonction du mode d'édition et l'a remplacée par une nouvelle solution qui règle la vue du contenu sur la position correcte chaque fois que layoutSubviews est appelé, ce qui entraîne automatiquement animation lors du passage au mode édition:

- (void)layoutSubviews
{
    CGRect b = [self bounds];
    b.size.height -= 1; // leave room for the separator line
    b.size.width += 30; // allow extra width to slide for editing
    b.Origin.x -= (self.editing) ? 0 : 30; // start 30px left unless editing
    [contentView setFrame:b];
    [super layoutSubviews];
}

En procédant ainsi, j'ai pu supprimer setFrame: override trouvé dans ABTableViewCell.m, car sa logique précédente et ses ajouts se trouvent maintenant dans layoutSubviews. 

J'ai défini un arrière-plan gris clair sur les cellules pour vérifier qu'un arrière-plan personnalisé fonctionne correctement sans nous permettre de voir en arrière-plan lorsqu'il se déplace d'avant en arrière et qu'il semble fonctionner parfaitement.

Merci encore à Craig et à tous ceux qui ont étudié la question.

GitHub s’engage pour cette solution: ( link )

24
Adam Alexander

Comment déplacez-vous le texte actuellement? Ou plus précisément, dans quelle méthode UITableViewCell effectuez-vous le déplacement?

D'après mon expérience, le fait de surcharger la méthode layoutSubviews et de définir l'image ici sera automatiquement intégré à une animation.

Par exemple:

- (void)layoutSubviews {
    if (self.editing) {
        [titleLabel setFrame:CGRectMake(62, 6, 170, 24)];
    }
    else {
        [titleLabel setFrame:CGRectMake(30, 6, 200, 24)];
    }
    [super layoutSubviews];
}
10
Craig Otis

Pour un contrôle total sur l'édition dans votre cellule personnalisée, vous devez remplacer la méthode willTransitionToState dans votre sous-classe UITableViewCell et vérifier le masque d'état.

- (void)willTransitionToState:(UITableViewCellStateMask)state
{
    NSString *logStr = @"Invoked";
    if ((state & UITableViewCellStateShowingEditControlMask)
        != 0) {
        // you need to move the controls in left
        logStr = [NSString stringWithFormat:@"%@
                  %@",logStr,@"UITableViewCellStateShowingEditControlMask"];
    }
    if ((state & UITableViewCellStateShowingDeleteConfirmationMask)
        != 0) {
        // you need to hide the controls for the delete button
        logStr = [NSString stringWithFormat:@"%@
                  %@",logStr,@"UITableViewCellStateShowingDeleteConfirmationMask"];
    }
    NSLog(@"%@",logStr);
    [super willTransitionToState:state];
}

vous pouvez aussi remplacer layoutSubviews

- (void)layoutSubviews {
    // default place for label
    CGRect alarmTimeRect = CGRectMake(37, 7, 75, 30);
    if (self.editing && !self.showingDeleteConfirmation) {
        // move rect in left
        alarmTimeRect = CGRectMake(77, 7, 75, 30);
    }
    [alarmTimeLabel setFrame:alarmTimeRect];
    [super layoutSubviews];
}
9
UIBuilder

Pour gérer également les glissements: (auto.édition et&! Auto.showingDeleteConfirmation)

8
ben

Je venais aussi de poser cette question: comment animer un mode d’édition personnalisé? Je n’aimais pas vraiment la solution ici, j’ai donc décidé de réfléchir un peu et de trouver une solution différente. Je ne sais pas si c'est mieux, mais je préfère celui-là. J'ai donc décidé de le partager ici:

Dans la cellule personnalisée (héritée de UITableViewCell), surchargez simplement setEditing:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];

    if (animated) {
        [UIView beginAnimations:@"setEditingAnimation" context:nil];
        [UIView setAnimationDuration:0.3];
    }

    if (editing) {
        /* do your offset and resize here */
    } else {
        /* return to the original here*/
    }

    if (animated)
        [UIView commitAnimations];
}

Je ne vérifie pas si la valeur d'édition est la même, mais c'est juste une idée de la façon dont je l'ai fait. 

3
Sauleil

Il y a en fait quatre états édités.

UITableViewCellStateDefaultMask = 0

UITableViewCellStateShowingEditControlMask = 1 << 0 = 0b01 = 1 en définissant le paramètre tableView.edit = YES

UITableViewCellStateShowingDeleteConfirmationMask = 1 << 1 = 0b10 = 2 en balayant pour supprimer

UITableViewCellStateShowingEditControlMask et UITableViewCellStateShowingDeleteConfirmationMask = (1 << 1) & (1 << 0) = 0b11 = 3 en définissant la tableView.edit = YES et en appuyant sur le bouton "arrêter"

Ce qui m'a dérouté, c'est que lorsque l'état 3 passe à l'état 1, aucun sélecteur n'appelle qui ne fait pas usage de self.showingDeleteConfirmation pour changer de disposition dans iOS7 . Par conséquent, afin de clarifier les états 2 à 3, J'ai ajouté une variable d'instance pour l'implémenter. Ça marche bien pour moi.

-(void)willTransitionToState:(UITableViewCellStateMask)state{
    [super willTransitionToState:state];

    //Custom delete button from
    //http://stackoverflow.com/questions/19159476/uitableviewcelldeleteconfirmationcontrol-issue
    if((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask){
        [self recurseAndReplaceSubViewIfDeleteConfirmationControl:self.subviews];
        [self performSelector:@selector(recurseAndReplaceSubViewIfDeleteConfirmationControl:) withObject:self.subviews afterDelay:0];
    }

    _swipeDelete = NO;

    if(state==2){
        //Swipe to delete confirm
        _swipeDelete = YES;
    }

    if(state==3){
        //Edit state to delete confirm
    }
}

- (void)layoutSubviews{

    [super layoutSubviews];

    self.contentView.frame = CGRectMake(10, 5.0, 300, self.frame.size.height - 10);

    if(self.editing){
        self.contentView.frame = CGRectMake(40, 5.0, 270, self.frame.size.height - 10);
    }

    if(_swipeDelete){
        self.contentView.frame = CGRectMake(10, 5.0, 300, self.frame.size.height - 10);
    }
}
0
Henry