2013-05-02 13 views
5

So che questo è stato chiesto molte volte, ma per favore abbiate pazienza con me dato che ho provato per un giorno a trovare una soluzione.UITableVisualizza le celle disinserite ricaricando le vecchie immagini

Ho un parser rss, una tabella con: testo, dettagli e immagini impostati su AccessoryDisclosureIndicator.view.

Le immagini si caricano perfettamente con semplice GCD asincrono: scorrimento veloce per centinaia di risultati. NESSUN ritardo, NESSUN errore se ho una buona connessione.

Il problema è, per una frazione di secondo - sfarfallio sul carico, perché la cella viene riutilizzata. Anche se la connessione è scadente, a volte lascerà l'immagine sfocata MA il testo/i dettagli sono corretti, solo l'immagine è vecchia ... Quindi lasciatemi ripetere, il testo/dettagli si aggiorna bene e MAI viene ri-accodato in modo errato, solo il l'immagine a volte raramente viene messa in coda in modo errato su connessioni errate/lo scorrimento delle convulsioni avanti e indietro velocemente.

La mia domanda, qualcuno può aiutarmi a memorizzare/codificare il cell.accs.views? Ho provato a impostare i CellID ma ho avuto problemi con la mia implementazione. Il mio codice qui sotto funziona alla grande se la connessione non rallenta mai, basta un leggero sfarfallio per ri-accodare una cella che non mi dispiace.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
static NSString *CellIdentifier = @"Cell"; 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

if (cell == nil) { 
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 

    [cell.textLabel setNumberOfLines:3]; 
    cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0]; 

    [cell.detailTextLabel setNumberOfLines:3]; 
    cell.detailTextLabel.font = [UIFont systemFontOfSize:12.0]; 
    cell.detailTextLabel.textColor = [UIColor blackColor]; 
} 

RSSItem * rssItem = (RSSItem *)(_rssParser.rssItems)[indexPath.row]; 

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
dispatch_async(queue, ^{ 
    //This is what you will load lazily 
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:rssItem.imageURL]]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 

     UIImageView *accImageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:data ]]; 

     [accImageView setFrame:CGRectMake(0, 0, accImageView.image.size.width, 92)]; 
     cell.accessoryView = accImageView; 

     //[cell setNeedsLayout];//not needed 
    }); 
}); 

[cell.textLabel setText:rssItem.title]; 
[cell.detailTextLabel setText:rssItem.summary]; 

return cell;} 

risposta

0

Ecco cosa ho fatto utilizzando SDWebImage, sono stato costretto a utilizzare un segnaposto per tutte le celle, ho impostato quelle senza immagini su una piccola icona a forma di chevron. Funziona come previsto. Grazie a tutti.

RSSItem * rssItem = (RSSItem *)(_rssParser.rssItems)[indexPath.row]; 

if (rssItem.imageURL == nil){ 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; 
    [imageView setImageWithURL:[NSURL URLWithString:rssItem.imageURL] placeholderImage:[UIImage imageNamed:@"chevron.png"]]; 
    cell.accessoryView = imageView; 
}else{ 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 110, 96)]; 
    imageView.contentMode = UIViewContentModeScaleAspectFit; 
    [imageView setImageWithURL:[NSURL URLWithString:rssItem.imageURL] placeholderImage:[UIImage imageNamed:@"loading.png"]]; 
    cell.accessoryView = imageView; 
} 

[cell.textLabel setText:rssItem.title]; 
[cell.detailTextLabel setText:rssItem.summary]; 

return cell; 
1

Hai appena bisogno di cancellare la vecchia immagine quando riutilizzando la cella.

Alla fine, si può semplicemente aggiungere:

cell.accessoryView = nil; 
+0

I miei problemi sono apparentemente più profondo di quello, grazie tho – JoshP

1

Il problema è, per una frazione di secondo - hanno sfarfallio sul carico, perché la cella viene riutilizzato

Ma è la tua risposta proprio lì. La cella viene riutilizzata e non vedo nulla nel codice che rimuove l'immagine. Se l'immagine non è l'immagine giusta per questa riga (poiché viene riutilizzata in una riga diversa), è necessario impostare cell.accessoryView su nil (o su una vista immagine con un'immagine segnaposto) ora. L'immagine giusta non arriverà fino a in seguito (dispatch_async).

Tuttavia, il codice che si sta utilizzando è molto imperfetto, perché sta recuperando l'immagine da Internet ogni volta, anche se abbiamo già visto questa riga e quindi già scaricato l'immagine. Il mio libro ha un codice molto migliore per caricare le immagini in una vista tabella, scaricandole se necessario e memorizzandole successivamente. Essa mostra anche come fermare il download se la riga viene fatto scorrere fuori dal tavolo prima che l'immagine arriva, risparmiando così la larghezza di banda:

http://www.apeth.com/iOSBook/ch37.html#_http_requests

Scorrere verso il basso per cui si dice "Supponiamo, per esempio, è necessario scarica le immagini come anteprime nelle celle di un UITableView. Consideriamo come queste immagini possono essere fornite pigramente su richiesta. "

+0

Sì ... ho capito che ho bisogno di delagate una cache per evitare Redownloading, ma sì ... ho anche bisogno di capire come per contrassegnare le cellule ... Sono gunna leggi i tuoi smottamenti, ma sì sono un noob – JoshP

+0

Progetto di esempio di download effettivo qui: https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch37p920downloader/p754p772downloader – matt

+0

Il tuo esempio sembra qualcosa di simile a provare, grazie. Voglio darmi un suggerimento su come eseguire il mio rss.items.title/text/imgurl/array tramite addobject: d? – JoshP

10

Il problema chiave è che prima di effettuare il dispatch_async per ottenere la nuova immagine (che potrebbe richiedere alcuni minuti, anche i secondi su una connessione lenta), ripristinare quello vecchio o caricarlo con un'immagine segnaposto.

Due questioni secondarie:

  1. essere consapevoli del fatto che è possibile, se si utilizza una rete molto lenta, che per il momento l'immagine torna, che si potrebbe avere scorrere la cellula fuori dallo schermo (ad esempio, immaginare che hai fatto scattare la vista di scorrimento verso l'alto e poi, mentre le immagini erano ancora in fase di caricamento, l'hai riavviata indietro). Dovresti davvero assicurarti che la cella sia ancora visibile prima di provare a impostare l'immagine.

    Potete farlo controllando (sulla coda principale) se la cella è ancora visibile:

    dispatch_async(dispatch_get_main_queue(), ^{ 
        UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; 
        if (updateCell != nil) { 
         // update image in `updateCell` 
        } 
    }); 
    

    Si prega di notare, questo metodo UITableView, cellForRowAtIndexPath, non deve essere confuso con il metodo UITableViewDataSource, tableView:cellForRowAtIndexPath:. Il primo semplicemente controlla se la cella è visibile e, in caso affermativo, restituisce un puntatore ad essa. Quest'ultima, ovviamente, è il metodo a cui fai riferimento nella tua domanda, per creare e configurare celle di visualizzazione tabella.

    Si noti inoltre che questa idea, che si utilizza il NSIndexPath per verificare se la cella è ancora visibile, presuppone che nessuna riga aggiuntiva potrebbe essere stata inserita nella vista tabella nel frattempo. Questo spesso non è un'ipotesi valida, quindi potresti persino dover tornare al modello sottostante e ricalcolare il indexPath per questa cella prima di procedere con la logica precedente per vedere se quella cella è ancora visibile o meno.

  2. Potresti non voler utilizzare una coda globale perché in una rete molto lenta le tue richieste potrebbero iniziare a tornare indietro e si potrebbero finire con un sacco di richieste di rete simultanee. Questo è un problema perché (a) il sistema non può fare più di 4 o 5 richieste simultanee alla volta, comunque; e (b) userai il numero limitato di thread worker forniti da iOS. Non dovresti avere più di 4 o 5 richieste simultanee (se ne hai di più, non solo non vedrai alcun guadagno in termini di prestazioni, ma in realtà blocceranno e alla fine scadono). Ti suggerirei di prendere in considerazione l'utilizzo di un NSOperationQueue per il quale devi impostare maxConcurrentOperationCount su quattro o cinque.

    Inoltre, questo offre un'ottimizzazione finale, in particolare rendendo queste richieste di sottoclasse NSOperation cancellabili (e, ovviamente, facendo in modo che la logica di cancellazione annulli effettivamente la richiesta di rete in esso contenuta). Pertanto, se si scorre rapidamente fino alla centesima riga nella tabella, non si desidera che le celle visibili siano in attesa del download delle immagini per le 99 righe precedenti. Se si annulla la richiesta di immagine per le celle che si allontanano dallo schermo, risolve questo problema.

Se si vuole per diagnosticare il comportamento della tua app su una connessione di rete molto lento, io suggerirei che si ottiene il condizionatore di rete, che può avere il Mac simulare qualcosa che va da tutta velocità per un vantaggio davvero di scarsa qualità rete sul simulatore. Nel menu Xcode in Xcode, scegli "Apri strumento di sviluppo ..." e quindi seleziona "Altri strumenti di sviluppo". Dopo aver effettuato il login, verrà visualizzata un'opzione per "I/O hardware per Xcode". Lo strumento Network Conditioner è lì. Puoi testare la tua app negli scenari peggiori, che potrebbero farti pensare non solo ai due problemi che ho descritto sopra, ma anche a funzioni più avanzate come la memorizzazione nella cache di immagini e simili.

Si desidera veramente memorizzare nella cache le immagini, almeno nella memoria persistente, in modo ideale sia per la RAM che per la memorizzazione persistente. Esistono le categorie UIImageView che affrontano entrambi i problemi asincroni di cui sopra, oltre a offrire alcuni caching: due che potresti considerare sono SDWebImage e AFNetworking.

+0

Ohhh gawd, con il condizionatore di linea ON Vedo che la cella passa ciclicamente tra 4 o 5 immagini finché non ottiene quello giusto ... Perché è così difficile? Sono tutto per il caching, ma sto avendo problemi ad aggiungere i tag e controllarli o non riesco a trovare un buon tutorial ... – JoshP

+1

@JoshP O [SDWebImage] (https://github.com/rs/SDWebImage) o [AFNetworking] (https://github.com/AFNetworking/AFNetworking) La categoria 'UIImageView' renderà la tua vita * molto * più facile. Se vuoi davvero scrivere da solo, fammi sapere e posso aiutarti a trovare buone risposte. Quando ho scritto il mio, uso personalmente una combinazione di 'NSCache' per la cache RAM e la cartella' Documents' per la memoria cache persistente (anche se penso che la convenzione ora usi una cartella 'Cache' separata). Ti suggerirei di rimanere fedele ad AFNetworking o SDWebImage. – Rob

+0

Cercherò di provarli, la gente lo fa tutti i giorni e non riesco a trovare un buon tutorial, mi fa solo impazzire la mente che il tavolo dimentica che è davvero scorrevole? Come può ricordare il testo ma dimenticare l'immagine? Questo sembra qualcosa che la mela dovrebbe risolvere automagicamente :) Sto per non desinare le celle ma è ridicolo. Grazie, nella mia mente so che può essere fatto modificando il mio codice sopra senza classi esterne e forse solo una cache di appdel, ma come ho detto ho bisogno di cercare di più per esempi migliori. – JoshP

Problemi correlati