porque mis las fototeca fotos desaparecieron desaparecen desactivo cargan carga borraron borran ios objective-c uitableview lazy-loading sdwebimage

ios - las - si desactivo la fototeca de icloud se borran mis fotos



SDWebImage no carga imágenes remotas hasta que el desplazamiento (4)

Este es un ejemplo y debe implementar esto para su propósito.
su delegado UITableView:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { YourCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YourCustomTableViewCellReuseIdentifier"]; if (!cell) { cell = [[[YourCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } NSString *imageURL = // ... get image url, typically from array [cell loadImageWithURLString:imageURL forIndexPath:indexPath]; return cell; }

su archivo UITableViewCell .h personalizado :

#import <UIKit/UIKit.h> #import "UIImageView+WebCache.h" #import "SDImageCache.h" @interface YourCustomTableViewCell { NSIndexPath *currentLoadingIndexPath; } - (void)loadImageWithURLString:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath; @end

su archivo personalizado UITableViewCell .m :

// ... some other methods - (void)loadImageWithURLString:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath { currentLoadingIndexPath = indexPath; [self.imageView cancelCurrentImageLoad]; [self.imageView setImage:nil]; NSURL *imageURL = [NSURL URLWithString:urlString]; [self.imageView setImageWithURL:imageURL placeholderImage:nil options:SDWebImageRetryFailed completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { if (currentLoadingIndexPath != indexPath) { return; } if (error) { ... // handle error } else { [imageView setImage:image]; } }]; } // ... some other methods

currentLoadingIndexPath necesario para detectar si reutilizamos esta celda para otra imagen en lugar de la imagen que se descargó mientras el usuario se desplaza por la vista de tabla.

Estoy usando la biblioteca SDWebImage para cargar imágenes remotas en una vista de tabla que usa una clase de celda personalizada que he creado. Simplemente uso

[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"loading.jpg"]];

en cellForRowAtIndexPath: ahora el problema es que carga imágenes solo en las celdas visibles y no para las celdas que están fuera de la pantalla, para lo cual tengo que desplazarme hacia arriba y hacia abajo para que se carguen. ¿Hay alguna forma en que pueda cargar todas las imágenes sin tener que desplazar la vista de tabla? ¡¡Gracias por adelantado!!


Solo tuve que resolver este problema exacto y no quería la sobrecarga del captador previo. Debe haber algunas cosas extra bajo el capó que suceden con la propiedad integrada imageView que impide la carga, porque un nuevo UIImageView funciona bien.

Mi solución es bastante clara si no te importa (o ya lo estás) usar una subclase de UITableViewCell:

  1. Subclase UITableViewCell.
  2. En tu subclase, oculta self.imageView.
  3. Cree su propia subvista UIImageView y configure la imagen de esta vista.

Aquí hay una versión modificada de mi propio código (no documentado aquí está configurando el marco para que coincida con el tamaño y la posición de las carátulas de álbumes de la aplicación iOS Photo):

YourTableCell.h

@interface YourTableCell : UITableViewCell @property (nonatomic, strong) UIImageView *coverPhoto; @end

YourTableCell.m

@implementation YourTableCell @synthesize coverPhoto; - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { self.imageView.image = nil; self.coverPhoto = [[UIImageView alloc] init]; // Any customization, such as initial image, frame bounds, etc. goes here. [self.contentView addSubview:self.coverPhoto]; } return self; } //... @end

YourTableViewController.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; YourTableCell *cell = (YourTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //... [cell.coverPhoto setImageWithURL:coverUrl placeholderImage:nil options:SDWebImageCacheMemoryOnly]; //... }


Si desea precomprimir filas, puede responder a los métodos UIScrollViewDelegate para determinar cuándo se realiza el desplazamiento de la tabla y desencadenar una captación previa de las filas. Puede realizar la SDWebImagePrefetcher utilizando SDWebImagePrefetcher (en mi respuesta original, no me SDWebImagePrefetcher esta clase útil, pero parece funcionar relativamente bien ahora):

- (void)viewDidLoad { [super viewDidLoad]; // the details don''t really matter here, but the idea is to fetch data, // call `reloadData`, and then prefetch the other images NSURL *url = [NSURL URLWithString:kUrlWithJSONData]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { if (connectionError) { NSLog(@"sendAsynchronousRequest error: %@", connectionError); return; } self.objects = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; [self.tableView reloadData]; [self prefetchImagesForTableView:self.tableView]; }]; } // some of the basic `UITableViewDataDelegate` methods have been omitted because they''re not really relevant

Aquí está el simple cellForRowAtIndexPath (no del todo relevante, pero solo muestra que si usa SDWebImagePrefetcher , no tiene que perder el tiempo con cellForRowAtIndexPath :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; NSAssert([cell isKindOfClass:[CustomCell class]], @"cell should be CustomCell"); [cell.customImageView setImageWithURL:[self urlForIndexPath:indexPath] placeholderImage:nil]; [cell.customLabel setText:[self textForIndexPath:indexPath]]; return cell; }

Estos métodos UIScrollViewDelegate recuperan más filas cuando termina el desplazamiento

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { // if `decelerate` was true for `scrollViewDidEndDragging:willDecelerate:` // this will be called when the deceleration is done [self prefetchImagesForTableView:self.tableView]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { // if `decelerate` is true, then we shouldn''t start prefetching yet, because // `cellForRowAtIndexPath` will be hard at work returning cells for the currently visible // cells. if (!decelerate) [self prefetchImagesForTableView:self.tableView]; }

Obviamente necesitas implementar una rutina de captación previa. Esto obtiene los valores de NSIndexPath para las celdas en cada lado de las celdas visibles, obtiene sus URL de imagen y luego capta previamente esos datos.

/** Prefetch a certain number of images for rows prior to and subsequent to the currently visible cells * * @param tableView The tableview for which we''re going to prefetch images. */ - (void)prefetchImagesForTableView:(UITableView *)tableView { NSArray *indexPaths = [self.tableView indexPathsForVisibleRows]; if ([indexPaths count] == 0) return; NSIndexPath *minimumIndexPath = indexPaths[0]; NSIndexPath *maximumIndexPath = [indexPaths lastObject]; // they should be sorted already, but if not, update min and max accordingly for (NSIndexPath *indexPath in indexPaths) { if (indexPath.section < minimumIndexPath.section || (indexPath.section == minimumIndexPath.section && indexPath.row < minimumIndexPath.row)) minimumIndexPath = indexPath; if (indexPath.section > maximumIndexPath.section || (indexPath.section == maximumIndexPath.section && indexPath.row > maximumIndexPath.row)) maximumIndexPath = indexPath; } // build array of imageURLs for cells to prefetch NSMutableArray *imageURLs = [NSMutableArray array]; indexPaths = [self tableView:tableView priorIndexPathCount:kPrefetchRowCount fromIndexPath:minimumIndexPath]; for (NSIndexPath *indexPath in indexPaths) [imageURLs addObject:[self urlForIndexPath:indexPath]]; indexPaths = [self tableView:tableView nextIndexPathCount:kPrefetchRowCount fromIndexPath:maximumIndexPath]; for (NSIndexPath *indexPath in indexPaths) [imageURLs addObject:[self urlForIndexPath:indexPath]]; // now prefetch if ([imageURLs count] > 0) { [[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:imageURLs]; } }

Estos son los métodos de utilidad para obtener NSIndexPath para las filas inmediatamente anteriores a las celdas visibles, así como las que siguen inmediatamente a las celdas visibles:

/** Retrieve NSIndexPath for a certain number of rows preceding particular NSIndexPath in the table view. * * @param tableView The tableview for which we''re going to retrieve indexPaths. * @param count The number of rows to retrieve * @param indexPath The indexPath where we''re going to start (presumably the first visible indexPath) * * @return An array of indexPaths. */ - (NSArray *)tableView:(UITableView *)tableView priorIndexPathCount:(NSInteger)count fromIndexPath:(NSIndexPath *)indexPath { NSMutableArray *indexPaths = [NSMutableArray array]; NSInteger row = indexPath.row; NSInteger section = indexPath.section; for (NSInteger i = 0; i < count; i++) { if (row == 0) { if (section == 0) { return indexPaths; } else { section--; row = [tableView numberOfRowsInSection:section] - 1; } } else { row--; } [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; } return indexPaths; } /** Retrieve NSIndexPath for a certain number of following particular NSIndexPath in the table view. * * @param tableView The tableview for which we''re going to retrieve indexPaths. * @param count The number of rows to retrieve * @param indexPath The indexPath where we''re going to start (presumably the last visible indexPath) * * @return An array of indexPaths. */ - (NSArray *)tableView:(UITableView *)tableView nextIndexPathCount:(NSInteger)count fromIndexPath:(NSIndexPath *)indexPath { NSMutableArray *indexPaths = [NSMutableArray array]; NSInteger row = indexPath.row; NSInteger section = indexPath.section; NSInteger rowCountForSection = [tableView numberOfRowsInSection:section]; for (NSInteger i = 0; i < count; i++) { row++; if (row == rowCountForSection) { row = 0; section++; if (section == [tableView numberOfSections]) { return indexPaths; } rowCountForSection = [tableView numberOfRowsInSection:section]; } [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; } return indexPaths; }

Hay mucho allí, pero en realidad, SDWebImage y su SDWebImagePrefetcher están haciendo un gran trabajo.

Incluyo mi respuesta original a continuación para completar.

Respuesta original:

Si desea realizar una búsqueda SDWebImage con SDWebImage , puede hacer algo como lo siguiente:

  1. Agregue un bloque de finalización a su llamada setImageWithURL :

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"%s", __FUNCTION__); static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; TableModelRow *rowData = self.objects[indexPath.row]; cell.textLabel.text = rowData.title; [cell.imageView setImageWithURL:rowData.url placeholderImage:[UIImage imageNamed:@"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { [self prefetchImagesForTableView:tableView]; }]; return cell; }

    Debo confesar que realmente no me gusta llamar a mi rutina de didFinishTableRefresh aquí (ojalá iOS tuviera algún buen método de delegado didFinishTableRefresh ), pero funciona, incluso si está llamando a la rutina más veces de las que realmente quisiera. Solo me aseguro a continuación de que la rutina a continuación se asegure de que no haga solicitudes redundantes.

  2. De todos modos, escribo una rutina de captación previa que busca, por ejemplo, las siguientes diez imágenes:

    const NSInteger kPrefetchRowCount = 10; - (void)prefetchImagesForTableView:(UITableView *)tableView { // determine the minimum and maximum visible rows NSArray *indexPathsForVisibleRows = [tableView indexPathsForVisibleRows]; NSInteger minimumVisibleRow = [indexPathsForVisibleRows[0] row]; NSInteger maximumVisibleRow = [indexPathsForVisibleRows[0] row]; for (NSIndexPath *indexPath in indexPathsForVisibleRows) { if (indexPath.row < minimumVisibleRow) minimumVisibleRow = indexPath.row; if (indexPath.row > maximumVisibleRow) maximumVisibleRow = indexPath.row; } // now iterate through our model; // `self.objects` is an array of `TableModelRow` objects, one object // for every row of the table. [self.objects enumerateObjectsUsingBlock:^(TableModelRow *obj, NSUInteger idx, BOOL *stop) { NSAssert([obj isKindOfClass:[TableModelRow class]], @"Expected TableModelRow object"); // if the index is within `kPrefetchRowCount` rows of our visible rows, let''s // fetch the image, if it hasn''t already done so. if ((idx < minimumVisibleRow && idx >= (minimumVisibleRow - kPrefetchRowCount)) || (idx > maximumVisibleRow && idx <= (maximumVisibleRow + kPrefetchRowCount))) { // my model object has method for initiating a download if needed [obj downloadImageIfNeeded]; } }]; }

  3. En la rutina de descarga, puede verificar si la descarga de la imagen ha comenzado y, de no ser así, iniciarla. Para hacer esto con SDWebImage , mantengo un puntero weak a la operación de imagen web en mi clase TableModelRow (la clase de modelo que respalda las filas individuales de mi tabla):

    @property (nonatomic, weak) id<SDWebImageOperation> webImageOperation;

    Luego hago que la rutina downloadImageIfNeeded comience una descarga si aún no lo ha hecho (se puede ver por qué era tan importante hacer esa weak ... Estoy comprobando si esta fila ya tiene una operación pendiente antes de comenzar otra). No estoy haciendo nada con la imagen descargada (excepto, para fines de depuración, registrar el hecho de que se realizó una descarga), sino simplemente descargar y dejar que SDImageWeb un seguimiento de la imagen almacenada en caché, por lo que cuando cellForRowAtIndexPath solicita posteriormente el imagen a medida que el usuario se desplaza hacia abajo, está allí, listo y esperando.

    - (void)downloadImageIfNeeded { if (self.webImageOperation) return; SDWebImageManager *imageManager = [SDWebImageManager sharedManager]; self.webImageOperation = [imageManager downloadWithURL:self.url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) { NSLog(@"%s: downloaded %@", __FUNCTION__, self.title); // I''m not going to do anything with the image, but `SDWebImage` has now cached it for me }]; }

    Parte de mí piensa que podría ser más robusto llamar imageManager.imageCache método de instancia de queryDiskCacheForKey , pero después de hacer algunas pruebas, no parece que sea necesario (y la downloadWithURL hace por nosotros, de todos modos).

Debo señalar que la biblioteca SDImageWeb tiene una clase SDWebImagePrefetcher (ver la documentación ). El nombre de la clase es increíblemente prometedor, pero mirando el código, con toda la deferencia a una biblioteca por lo demás excelente, esto no me parece muy sólido (por ejemplo, es una lista simple de URL para buscar y si lo haces de nuevo , cancela la lista anterior sin ninguna noción de "agregar a la cola" o algo así). Es una idea prometedora, pero un poco débil en la ejecución. Y cuando lo probé, mi UX sufrió notablemente.

Por lo tanto, me inclino a no usar SDWebImagePrefetcher (hasta que se mejore, al menos) y atenerme a mi técnica de SDWebImagePrefetcher rudimentaria. No es terriblemente sofisticado, pero parece funcionar.


Encontré el mismo problema, me pareció que UIImageView + WebCache cancelaba la última descarga cuando llegaba una nueva descarga.

No estoy seguro de si esta es la intención del autor. Así que escribo una nueva category de UIImageView base en SDWebImage.

Fácil de usar:

[cell.imageView mq_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] groupIdentifier:@"customGroupID" completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { }];

Para ver más: ImageDownloadGroup

Uso avanzado:

// create customGroup MQImageDownloadGroup *customGroup = [[MQImageDownloadGroup alloc] initWithGroupIdentifier:@"tableViewCellGroup"]; customGroup.maxConcurrentDownloads = 99; // add to MQImageDownloadGroupManage [[MQImageDownloadGroupManage shareInstance] addGroup:customGroup]; // use download group [cell.imageView mq_setImageWithURL:@"https://xxx" groupIdentifier:@"tableViewCellGroup" completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { }];