61 votes

Garder uitableview statique lors de l'insertion de lignes en haut de page

J'ai une tableView dans laquelle j'insère des lignes en haut.

Pendant que je fais cela, je veux que la vue actuelle reste complètement immobile, de sorte que les rangées n'apparaissent que si vous faites défiler vers le haut.

J'ai essayé de sauvegarder la position actuelle de la vue UIScroll sous-jacente et de réinitialiser la position après l'insertion des rangées, mais cela donne lieu à une oscillation, de haut en bas, bien qu'elle finisse par revenir au même endroit.

Existe-t-il un bon moyen d'y parvenir ?

Mise à jour : j'utilise beginUpdate, puis insertRowsAtIndexPath, endUpdates. Il n'y a pas d'appel reloadData.

scrollToRowAtIndexPath saute au sommet de la cellule actuelle (enregistrée avant l'ajout de lignes).

L'autre approche que j'ai essayée, qui se termine en exactement le bon rythme, mais avec un judder est.

save tableView currentOffset. (Underlying scrollView method)
Add rows (beginUpdates,insert...,endUpdates) 
reloadData ( to force a recalulation of the scrollview size )
Recalculate the correct new offset from the bottom of the scrollview
setContentOffset (Underlying scrollview method)

Le problème est que le reloadData provoque un bref défilement de la vue de défilement/tableau, puis le setContentOffset la ramène au bon endroit.

Existe-t-il un moyen de faire en sorte qu'une tableView détermine sa nouvelle taille sans lancer l'affichage ?

Envelopper le tout dans un beginAnimation commitAnimation n'aide pas beaucoup non plus.

Mise à jour 2 : Cela peut clairement être fait - voir l'application officielle de Twitter pour un lorsque vous tirez vers le bas pour les mises à jour.

48voto

AmitP Points 864

Il n'y a pas vraiment besoin de résumer la hauteur de toutes les rangées, le nouveau contentSize après le rechargement de la table représente déjà cela. Il suffit donc de calculer le delta de la hauteur contentSize et de l'ajouter au décalage actuel.

    ...
    CGSize beforeContentSize = self.tableView.contentSize;
    [self.tableView reloadData];
    CGSize afterContentSize = self.tableView.contentSize;

    CGPoint afterContentOffset = self.tableView.contentOffset;
    CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + afterContentSize.height - beforeContentSize.height);
    self.tableView.contentOffset = newContentOffset;
    ...

24voto

Mayank Yadav Points 101
-(void) updateTableWithNewRowCount : (int) rowCount
{    
    //Save the tableview content offset 
    CGPoint tableViewOffset = [self.tableView contentOffset];                                                                                                            

    //Turn of animations for the update block 
    //to get the effect of adding rows on top of TableView
    [UIView setAnimationsEnabled:NO];

    [self.tableView beginUpdates];                    

    NSMutableArray *rowsInsertIndexPath = [[NSMutableArray alloc] init];        

    int heightForNewRows = 0;

    for (NSInteger i = 0; i < rowCount; i++) {

        NSIndexPath *tempIndexPath = [NSIndexPath indexPathForRow:i inSection:SECTION_TO_INSERT];
        [rowsInsertIndexPath addObject:tempIndexPath];

        heightForNewRows = heightForNewRows + [self heightForCellAtIndexPath:tempIndexPath];                        
    }

    [self.tableView insertRowsAtIndexPaths:rowsInsertIndexPath withRowAnimation:UITableViewRowAnimationNone];                                                                            

    tableViewOffset.y += heightForNewRows;                                                                                 

    [self.tableView endUpdates]; 

    [UIView setAnimationsEnabled:YES];

    [self.tableView setContentOffset:tableViewOffset animated:NO];           
}

-(int) heightForCellAtIndexPath: (NSIndexPath *) indexPath
{

    UITableViewCell *cell =  [self.tableView cellForRowAtIndexPath:indexPath];

    int cellHeight   =  cell.frame.size.height;

    return cellHeight;
}

Il suffit de passer le nombre de lignes des nouvelles lignes à insérer en haut.

21voto

an0 Points 4900

La méthode de @Dean pour utiliser un cache d'image est trop compliquée et je pense qu'elle détruit la réactivité de l'interface utilisateur.

Une façon correcte : Utilisez une sous-classe de UITableView et surchargez la fonction -setContentSize : dans lequel vous pouvez, d'une manière ou d'une autre, calculer combien la vue de la table est poussée vers le bas et compenser cela en réglant le paramètre contentOffset .

Il s'agit d'un exemple de code simple pour gérer la situation la plus simple où toutes les insertions se produisent en haut de la vue du tableau :

@implementation MyTableView

- (void)setContentSize:(CGSize)contentSize {
        // I don't want move the table view during its initial loading of content.
    if (!CGSizeEqualToSize(self.contentSize, CGSizeZero)) {
        if (contentSize.height > self.contentSize.height) {
            CGPoint offset = self.contentOffset;
            offset.y += (contentSize.height - self.contentSize.height);
            self.contentOffset = offset;
        }
    }
    [super setContentSize:contentSize];
}

@end

17voto

Andrey Z. Points 3298

A eu le même problème et a trouvé une solution.

    save tableView currentOffset. (Underlying scrollView method)
    //Add rows (beginUpdates,insert...,endUpdates) // don't do this!
    reloadData ( to force a recalulation of the scrollview size )
    add newly inserted row heights to contentOffset.y here, using tableView:heightForRowAtIndexPath:
    setContentOffset (Underlying scrollview method)

comme ça :

- (CGFloat) firstRowHeight
{
    return [self tableView:[self tableView] heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}

...
CGPoint offset = [[self tableView] contentOffset];
[self tableView] reloadData];
offset.y += [self firstRowHeight];
if (offset.y > [[self tableView] contentSize].height) {
    offset.y = 0;
}
[[self tableView] setContentOffset:offset];
...

fonctionne parfaitement, sans pépins.

9voto

Walt Sellers Points 1706

J'ai fait quelques tests avec un projet d'échantillon de données de base et j'ai obtenu qu'il reste immobile pendant que de nouvelles cellules sont ajoutées au-dessus de la cellule supérieure visible. Ce code devrait être ajusté pour les tableaux avec un espace vide à l'écran, mais une fois l'écran rempli, il fonctionne bien.

static CGPoint  delayOffset = {0.0};

- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
    if ( animateChanges )
        [self.tableView beginUpdates];
    delayOffset = self.tableView.contentOffset; // get the current scroll setting
}

Ajouté aux points d'insertion des cellules. Vous pouvez faire la soustraction inverse pour la suppression des cellules.

    case NSFetchedResultsChangeInsert:

        delayOffset.y += self.tableView.rowHeight;  // add for each new row
        if ( animateChanges )   
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
        break;

et enfin

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    if ( animateChanges )   
    {
        [self.tableView setContentOffset:delayOffset animated:YES];
        [self.tableView endUpdates];
    }
    else
    {
        [self.tableView reloadData];
        [self.tableView setContentOffset:delayOffset animated:NO];
    }
}

Avec animateChanges = NO, je ne voyais rien bouger lorsque les cellules étaient ajoutées.

En testant avec animateChanges = YES, le "judder" était présent. Il semble que l'animation de l'insertion des cellules n'avait pas la même vitesse que le défilement animé du tableau. Alors que le résultat final pourrait se terminer avec des cellules visibles exactement là où elles ont commencé, l'ensemble du tableau semble se déplacer de 2 ou 3 pixels, puis revenir en arrière.

Si les vitesses d'animation peuvent être rendues égales, il peut sembler rester en place.

Cependant, lorsque j'appuyais sur le bouton pour ajouter des rangs avant la fin de l'animation précédente, l'animation s'arrêtait brusquement et la suivante démarrait, ce qui entraînait un changement brusque de position.

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