7

Ok, quindi quello che mi serve è di impedire lo scrolling di tableview quando nuovi elementi vengono aggiunti al di sopra della riga visibile corrente. Sto usando NSFetchedResultsController con molti oggetti (come 10.000) e reloadData funziona abbastanza bene quando non sto toccando contentOffset.Rendi UITableView in posizione di scorrimento quando aggiungi le celle in cima

Tuttavia, quando sto cercando di manipolare contentOffset per mantenere la posizione di scorrimento quando vengono inserite nuove voci, inizia a bloccare l'interfaccia utente per 300 ms, il che non va bene per me.

Qualcuno può suggerire una soluzione migliore (più veloce) su come mantenere tableview nella stessa cella quando vengono aggiunti nuovi elementi nella parte superiore?

E questo è il codice che sto usando attualmente per tracciare inserimenti e spostare contenutiOffset. Non supporta animazioni e diverse dimensioni della cella.

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
     atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 
     newIndexPath:(NSIndexPath *)newIndexPath 
{ 
    switch(type) { 
     case NSFetchedResultsChangeInsert: 
      [_insertions addObject:@(newIndexPath.row)]; 
      break; 
    } 
} 


- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 
    _insertions = @[].mutableCopy; 
} 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {   
    NSMutableArray *copy = _insertions.mutableCopy; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     NSSortDescriptor *lowestToHighest = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]; 
     [copy sortUsingDescriptors:[NSArray arrayWithObject:lowestToHighest]]; 

     dispatch_sync(dispatch_get_main_queue(), ^{ 

      for (NSNumber *i in copy) { 
       [self p_delayedAddRowAtIndex:[i integerValue]]; 
      } 
      [self.tableView reloadData]; 
     }); 
    }); 
} 

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

-(void)p_delayedAddRowAtIndex:(NSInteger)row { 

    NSIndexPath *firstVisibleIndexPath = [[self.tableView indexPathsForVisibleRows] firstObject]; 

    if (firstVisibleIndexPath.row >= row) { 

     CGPoint offset = [[self tableView] contentOffset]; 
     offset.y += [self firstRowHeight]; 

     if ([self.tableView visibleCells].count > self.tableView.frame.size.height/[self firstRowHeight]) { 
      [[self tableView] setContentOffset:offset]; 
     } 
    } 
} 
+0

Questo lavoro: istantanea 'firstVisibleIndexPath' in' controllerWillChangeContent'. In 'didChangeObject', tieni un conteggio del numero di righe inserite prima di' firstVisibleIndexPath'. Quindi in 'controllerDidChangeContent', calcola la nuova riga (aggiungendo il conteggio), ricarica la tabellaView e poi' scrollToRowAtIndexPath' nel nuovo indexPath. – pbasdf

+0

assicurati che la dimensione stimata delle celle non sia impostata –

risposta

2

Ok, dopo alcune risposte che mi hanno indirizzato nella giusta direzione, ho trovato questa soluzione. Funziona liscio anche se fa delle brutte coordinate matematiche e non supporta celle di dimensioni diverse. Spero che sarà utile per qualcuno.

-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 
    NSIndexPath *firstVisible = [[self.tableView indexPathsForVisibleRows] firstObject]; 
    self.topVisibleMessage = [self.fetchedResultsController objectAtIndexPath:firstVisible]; 

    NSIndexPath *topIndexPath = [self.fetchedResultsController indexPathForObject:self.topVisibleMessage]; 

    self.cellOffset = self.tableView.contentOffset.y - topIndexPath.row * [self firstRowHeight]; 
} 


-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 
    [self.tableView reloadData]; 

    if (self.topVisibleMessage) { 
     NSIndexPath *topIndexPath = [self.fetchedResultsController indexPathForObject:self.topVisibleMessage]; 
     CGPoint point = {0, topIndexPath.row * [self firstRowHeight] + self.cellOffset}; 

     [self.tableView setContentOffset:point]; 
    } 
} 
0

Il tuo compito è forse familiare con molte visualizzazioni di elenchi di applicazioni newsfeed. Hai guardato quelle applicazioni. Potrebbero avere qualche tipo di soluzione. Come so, l'unico modo per mantenere la posizione della vista tabella è scrollRectToVisible:animated:.

Molte applicazioni di social network hanno un approccio familiare per inserire nuove celle in alto. Quando ci sono aggiornamenti, viene visualizzata una piccola notifica nella parte superiore del messaggio con il messaggio ("5 nuovi elementi"), che alla selezione inserisce celle e scorrimento automatico verso l'alto.

+0

Ho pensato alla notifica visiva e all'aggiunta di celle alla spina, ma poiché utilizzo NSFetchedResultsController non ho accesso diretto all'origine dati. Qualche idea su come posso farlo? – kandreych

+0

@kandreych non usa NSFetchedResultsController forse. – Astoria

+0

frc fa molto lavoro di cache e sincronizzazione in background nella mia app dato che sì è fondamentalmente lettore di rss, quindi voglio davvero mantenerlo – kandreych