Carga de imagen asíncrona desde url dentro de una celda UITableView: la imagen cambia a una imagen incorrecta mientras se desplaza

158

He escrito dos formas de cargar imágenes asíncronamente dentro de mi celda UITableView. En ambos casos, la imagen se cargará bien, pero cuando desplazaré la tabla, las imágenes cambiarán varias veces hasta que el desplazamiento finalice y la imagen vuelva a la imagen correcta. No tengo idea de por qué está sucediendo esto.

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:
                                                       @"http://myurl.com/getMovies.php"]];
        [self performSelectorOnMainThread:@selector(fetchedData:)
                               withObject:data waitUntilDone:YES];
    });
}

-(void)fetchedData:(NSData *)data
{
    NSError* error;
    myJson = [NSJSONSerialization
              JSONObjectWithData:data
              options:kNilOptions
              error:&error];
    [_myTableView reloadData];
}    

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    // Return the number of rows in the section.
    // Usually the number of items in your array (the one that holds your list)
    NSLog(@"myJson count: %d",[myJson count]);
    return [myJson count];
}
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

        myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        if (cell == nil) {
            cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
        }

        dispatch_async(kBgQueue, ^{
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];

            dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
            });
        });
         return cell;
}

... ...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

            myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
            if (cell == nil) {
                cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
            }
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]];
    NSURLRequest* request = [NSURLRequest requestWithURL:url];


    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse * response,
                                               NSData * data,
                                               NSError * error) {
                               if (!error){
                                   cell.poster.image = [UIImage imageWithData:data];
                                   // do whatever you want with image
                               }

                           }];
     return cell;
}
Segev
fuente
55
Estás tratando de almacenar información en las celdas reales. Esto es malo, muy malo. Debe almacenar información en n array (o algo similar) y luego mostrarla en las celdas. La información en este caso es el UIImage real. Sí, cárguelo de forma asíncrona, pero cárguelo en una matriz.
Fogmeister
1
@Fogmeister ¿Te refieres poster? Es presumiblemente una vista de imagen en su celda personalizada, por lo que EXEC_BAD_ACCESS está haciendo perfectamente bien. Tiene razón en que no debe usar la celda como depósito de datos del modelo, pero no creo que eso sea lo que está haciendo. Simplemente le está dando a la celda personalizada lo que necesita para presentarse. Además, y este es un tema más sutil, me preocuparía almacenar una imagen, en sí misma, en su matriz de modelos que respalde su vista de tabla. Es mejor usar un mecanismo de almacenamiento en caché de imágenes y su objeto modelo debería recuperarse de ese caché.
Rob
1
Sí, exactamente mi punto. Mirando la solicitud (que se muestra en su totalidad) está descargando la imagen de forma asincrónica y colocándola directamente en el imageView en la celda. (Usando así la celda para almacenar los datos, es decir, la imagen). Lo que debería estar haciendo es hacer referencia a un objeto y solicitar la imagen de ese objeto (contenido en una matriz o en algún lugar). Si el objeto aún no tiene la imagen, debe devolver un marcador de posición y descargar la imagen. Luego, cuando la imagen se descargue y esté lista para mostrar, informe a la tabla para que pueda actualizar la celda (si está visible).
Fogmeister
1
Lo que está haciendo forzará la descarga cada vez que se desplace a esa celda en la tabla. Si las imágenes se almacenan de forma persistente depende de él, pero al menos guárdelas durante toda la vida de la vista de tabla.
Fogmeister
1
Exactamente: D De esa manera solo necesita recuperar la imagen de la URL una vez. Verá esto en cosas como Facebook Friend Picker. Cuando lo comienzas, todos los avatares son marcadores de posición grises. Luego, a medida que te desplazas, todos se llenan a medida que avanza. Pero luego, cuando regresa a una celda que se mostró anteriormente, mostrará instantáneamente la imagen ya descargada.
Fogmeister

Respuestas:

230

Suponiendo que está buscando una solución táctica rápida, lo que debe hacer es asegurarse de que la imagen de la celda esté inicializada y también que la fila de la celda todavía esté visible, por ejemplo:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (data) {
            UIImage *image = [UIImage imageWithData:data];
            if (image) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                    if (updateCell)
                        updateCell.poster.image = image;
                });
            }
        }
    }];
    [task resume];

    return cell;
}

El código anterior aborda algunos problemas derivados del hecho de que la celda se reutiliza:

  1. No está inicializando la imagen de la celda antes de iniciar la solicitud de fondo (lo que significa que la última imagen de la celda en cola aún estará visible mientras se descarga la nueva imagen). Asegúrese de nilla imagepropiedad de cualquier vista de imagen o de lo contrario verá el parpadeo de las imágenes.

  2. Un problema más sutil es que en una red realmente lenta, su solicitud asincrónica podría no finalizar antes de que la celda se desplace de la pantalla. Puede usar el UITableViewmétodo cellForRowAtIndexPath:(que no debe confundirse con el UITableViewDataSourcemétodo con un nombre similar tableView:cellForRowAtIndexPath:) para ver si la celda de esa fila todavía está visible. Este método volverá nilsi la celda no es visible.

    El problema es que la celda se ha desplazado cuando el método asincrónico se ha completado y, lo que es peor, la celda se ha reutilizado para otra fila de la tabla. Al verificar si la fila aún está visible, se asegurará de no actualizar accidentalmente la imagen con la imagen de una fila que desde entonces se ha desplazado fuera de la pantalla.

  3. Algo no relacionado con la pregunta en cuestión, todavía me sentí obligado a actualizar esto para aprovechar las convenciones modernas y API, en particular:

    • Usar en NSURLSessionlugar de enviar -[NSData contentsOfURL:]a una cola en segundo plano;

    • Use en dequeueReusableCellWithIdentifier:forIndexPath:lugar de dequeueReusableCellWithIdentifier:(pero asegúrese de usar el prototipo de celda o la clase de registro o NIB para ese identificador); y

    • Usé un nombre de clase que se ajusta a las convenciones de nomenclatura de Cocoa (es decir, comienza con la letra mayúscula).

Incluso con estas correcciones, hay problemas:

  1. El código anterior no almacena en caché las imágenes descargadas. Eso significa que si desplaza una imagen fuera de la pantalla y nuevamente en la pantalla, la aplicación puede intentar recuperar la imagen nuevamente. Quizás tenga la suerte de que los encabezados de respuesta de su servidor permitirán el almacenamiento en caché bastante transparente ofrecido por NSURLSessiony NSURLCache, pero si no, realizará solicitudes innecesarias del servidor y ofrecerá una experiencia de usuario mucho más lenta.

  2. No estamos cancelando solicitudes de celdas que se desplazan fuera de la pantalla. Por lo tanto, si se desplaza rápidamente a la fila 100, la imagen de esa fila podría acumularse detrás de las solicitudes de las 99 filas anteriores que ya ni siquiera son visibles. Siempre debe asegurarse de priorizar las solicitudes de celdas visibles para la mejor experiencia de usuario.

La solución más simple que aborda estos problemas es usar una UIImageViewcategoría, como se proporciona con SDWebImage o AFNetworking . Si lo desea, puede escribir su propio código para tratar los problemas anteriores, pero es mucho trabajo, y las UIImageViewcategorías anteriores ya lo han hecho por usted.

Robar
fuente
1
Gracias. Creo que necesitas editar tu respuesta. updateCell.poster.image = nilto cell.poster.image = nil;updateCell se llama antes de que se declare.
Segev
1
Mi aplicación usa mucho json, así AFNetworkingque definitivamente es el camino a seguir. Lo sabía pero era demasiado flojo para usarlo. Solo admiro cómo funciona el almacenamiento en caché con su simple línea de código. [imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
Segev
2
Intenté todo lo anterior y SDWebImage (en realidad se detuvo aquí y ni siquiera necesité probar AFNetworking) y esta fue, con mucho, la mejor opción. Gracias @Rob.
mondousage
1
Cuando termine de cargar la imagen y actualice la vista de la imagen, elimine el indicador de actividad. El único truco es que tiene que anticipar lo que sucede si la celda se desplaza fuera de la vista mientras se recupera la imagen y la celda se reutiliza para otra fila, tendrá que detectar la presencia de cualquier indicador de actividad existente y eliminar / actualizar no solo asuma que la celda está libre de un indicador existente.
Rob
1
La pregunta original era "¿por qué esto da como cellForRowAtIndexPathresultado un parpadeo de las imágenes cuando me desplazo rápidamente" y le expliqué por qué sucedió eso y cómo solucionarlo. Pero seguí explicando por qué incluso eso era insuficiente, describí algunos problemas más profundos y discutí por qué sería mejor usar una de esas bibliotecas para manejar esto con más gracia (priorizar solicitudes de celdas visibles, almacenamiento en caché para evitar redes redundantes solicitudes, etc.). No tengo claro qué más esperaba en respuesta a la pregunta de "¿cómo detengo las imágenes parpadeantes en la vista de mi tabla"?
Rob
15

/ * Lo hice de esta manera, y también lo probé * /

Paso 1 = Registre la clase de celda personalizada (en el caso de la celda prototipo en la tabla) o nib (en el caso de la punta personalizada para la celda personalizada) para una tabla como esta en el método viewDidLoad:

[self.yourTableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:@"CustomCell"];

O

[self.yourTableView registerNib:[UINib nibWithNibName:@"CustomTableViewCell" bundle:nil] forCellReuseIdentifier:@"CustomCell"];

Paso 2 = Use el método "dequeueReusableCellWithIdentifier: forIndexPath:" de UITableView de esta manera (para esto, debe registrar la clase o punta):

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
            CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell" forIndexPath:indexPath];

            cell.imageViewCustom.image = nil; // [UIImage imageNamed:@"default.png"];
            cell.textLabelCustom.text = @"Hello";

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // retrive image on global queue
                UIImage * img = [UIImage imageWithData:[NSData dataWithContentsOfURL:     [NSURL URLWithString:kImgLink]]];

                dispatch_async(dispatch_get_main_queue(), ^{

                    CustomTableViewCell * cell = (CustomTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
                  // assign cell image on main thread
                    cell.imageViewCustom.image = img;
                });
            });

            return cell;
        }
Nitesh Borad
fuente
1
¿Llamar a cellForRowAtIndexPath dentro del bloque final hace que todo se dispare por segunda vez?
Mark Bridges
@ MarkBridges, No. En realidad, estoy llamando al método cellForRowAtIndexPath de tableView aquí. No se confunda con el método de fuente de datos de tableView con el mismo nombre. Se requiere, se puede llamar como [self tableView: tableView cellForRowAtIndexPath: indexPath]; Espero que esto aclare tu confusión.
Nitesh Borad
14

Existen múltiples marcos que resuelven este problema. Sólo para nombrar unos pocos:

Rápido:

C objetivo:

kean
fuente
Agregue sus sugerencias si hay otros marcos que vale la pena considerar.
kean
3
En realidad SDWebImageno resuelve este problema. Puede controlar cuándo se descarga SDWebImagela imagen , pero asigne la imagen UIImageViewsin pedirle permiso para hacerlo. Básicamente, el problema de la pregunta aún no se resuelve con esta biblioteca.
Bartłomiej Semańczyk
El problema de la pregunta era que el autor no estaba verificando si la celda se reutilizaba o no. Este es un problema muy básico que se aborda en esos marcos, incluida SDWebImage.
kean
SDWebImage es muy lento desde iOS 8, era uno de mis frameworks favoritos, pero ahora estoy empezando a usar PinRemoteImage, que funciona realmente bien.
Joan Cardona
@ BartłomiejSemańczyk Tienes razón, este problema no es resuelto por SDWebimage
Jan
9

Swift 3

Escribo mi propia implementación ligera para el cargador de imágenes con NSCache. ¡Ninguna imagen celular parpadeando!

ImageCacheLoader.swift

typealias ImageCacheLoaderCompletionHandler = ((UIImage) -> ())

class ImageCacheLoader {
    
    var task: URLSessionDownloadTask!
    var session: URLSession!
    var cache: NSCache<NSString, UIImage>!
    
    init() {
        session = URLSession.shared
        task = URLSessionDownloadTask()
        self.cache = NSCache()
    }
    
    func obtainImageWithPath(imagePath: String, completionHandler: @escaping ImageCacheLoaderCompletionHandler) {
        if let image = self.cache.object(forKey: imagePath as NSString) {
            DispatchQueue.main.async {
                completionHandler(image)
            }
        } else {
            /* You need placeholder image in your assets, 
               if you want to display a placeholder to user */
            let placeholder = #imageLiteral(resourceName: "placeholder")
            DispatchQueue.main.async {
                completionHandler(placeholder)
            }
            let url: URL! = URL(string: imagePath)
            task = session.downloadTask(with: url, completionHandler: { (location, response, error) in
                if let data = try? Data(contentsOf: url) {
                    let img: UIImage! = UIImage(data: data)
                    self.cache.setObject(img, forKey: imagePath as NSString)
                    DispatchQueue.main.async {
                        completionHandler(img)
                    }
                }
            })
            task.resume()
        }
    }
}

Ejemplo de uso

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "Identifier")
    
    cell.title = "Cool title"

    imageLoader.obtainImageWithPath(imagePath: viewModel.image) { (image) in
        // Before assigning the image, check whether the current cell is visible
        if let updateCell = tableView.cellForRow(at: indexPath) {
            updateCell.imageView.image = image
        }
    }    
    return cell
}
Dmitrii Klassneckii
fuente
3
Te diría gracias. Pero el código tiene un pequeño problema. si let data = try? Datos (contenido de: url) {// reemplace url por ubicación. ayudaría a mucha gente.
Carl Hung
2
Con el código tal como está, descarga dos veces el archivo a través de la red: una vez en downloadTaks, una vez con Data (cntentsOf :). Debe ubicar el usuario en lugar de la url porque la tarea de descarga simplemente se descarga en la red y escribe los datos en un archivo temporal y le pasa el localUrl (ubicación en su caso). Por lo tanto, los datos deben apuntar a la URL local para que solo se lea desde el archivo.
Stéphane de Luca
En el Ejemplo de uso, ¿se supone que es "ImageCacheLoader.obtainImageWithPath (imagePath: viewModel.image) ......."?
Tim Kruger
no funcionará al desplazarse muy rápido, las imágenes se intercambiarán muchas veces debido a la reutilización de la celda.
Juan Boero
5

Aquí está la versión rápida (usando el código C objetivo de @Nitesh Borad):

   if let img: UIImage = UIImage(data: previewImg[indexPath.row]) {
                cell.cardPreview.image = img
            } else {
                // The image isn't cached, download the img data
                // We should perform this in a background thread
                let imgURL = NSURL(string: "webLink URL")
                let request: NSURLRequest = NSURLRequest(URL: imgURL!)
                let session = NSURLSession.sharedSession()
                let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
                    let error = error
                    let data = data
                    if error == nil {
                        // Convert the downloaded data in to a UIImage object
                        let image = UIImage(data: data!)
                        // Store the image in to our cache
                        self.previewImg[indexPath.row] = data!
                        // Update the cell
                        dispatch_async(dispatch_get_main_queue(), {
                            if let cell: YourTableViewCell = tableView.cellForRowAtIndexPath(indexPath) as? YourTableViewCell {
                                cell.cardPreview.image = image
                            }
                        })
                    } else {
                        cell.cardPreview.image = UIImage(named: "defaultImage")
                    }
                })
                task.resume()
            }
Chathuranga Silva
fuente
3

La mejor respuesta no es la forma correcta de hacer esto :(. En realidad, vinculó indexPath con el modelo, lo que no siempre es bueno. Imagine que se han agregado algunas filas durante la carga de la imagen. Ahora la celda para indexPath dada existe en la pantalla, pero la imagen ya no es correcto! La situación es poco probable y difícil de replicar, pero es posible.

¡Es mejor usar el enfoque MVVM, vincular la celda con viewModel en el controlador y cargar la imagen en viewModel (asignar la señal ReactiveCocoa con el método switchToLatest), luego suscribir esta señal y asignar la imagen a la celda! ;)

Debe recordar no abusar de MVVM. ¡Las vistas tienen que ser muy simples! ¡Mientras que ViewModels debería ser reutilizable! Es por eso que es muy importante vincular View (UITableViewCell) y ViewModel en el controlador.

badeleux
fuente
1
Sí, mi "solución táctica" de la ruta del índice (que no estaba recomendando, sino que era la edición más modesta para resolver el problema de OP) sufre este problema (pero solo si la vista de tabla continúa agregando / borrando filas). Y si este fenómeno se manifestó, podría solucionarlo de otras maneras (en lugar de buscar utilizando la misma ruta de índice, solo consulte el modelo para la fila adecuada). Pero esa solución táctica tiene problemas aún más atroces (que describo anteriormente) que el que planteas aquí. Si utiliza la UIImageViewsolución de categoría que le aconsejo, no existe tal problema con respecto a las rutas de índice.
Rob
2
Puede parecer un poco pedante, pero invocar cualquier tipo de lógica desde VIEW es abusar de esta arquitectura.
badeleux
3

En mi caso, no se debió al almacenamiento en caché de imágenes (Used SDWebImage). Fue debido a la falta de coincidencia de la etiqueta de la celda personalizada con indexPath.row.

En cellForRowAtIndexPath:

1) Asigne un valor de índice a su celda personalizada. Por ejemplo,

cell.tag = indexPath.row

2) En el hilo principal, antes de asignar la imagen, verifique si la imagen pertenece a la celda correspondiente haciendo coincidirla con la etiqueta.

dispatch_async(dispatch_get_main_queue(), ^{
   if(cell.tag == indexPath.row) {
     UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
     thumbnailImageView.image = tmpImage;
   }});
});
AG
fuente
2

Gracias "Rob" ... Tuve el mismo problema con UICollectionView y su respuesta me ayudó a resolver mi problema. Aquí está mi código:

 if ([Dict valueForKey:@"ImageURL"] != [NSNull null])
    {
        cell.coverImageView.image = nil;
        cell.coverImageView.imageURL=nil;

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            if ([Dict valueForKey:@"ImageURL"] != [NSNull null] )
            {
                dispatch_async(dispatch_get_main_queue(), ^{

                    myCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];

                    if (updateCell)
                    {
                        cell.coverImageView.image = nil;
                        cell.coverImageView.imageURL=nil;

                        cell.coverImageView.imageURL=[NSURL URLWithString:[Dict valueForKey:@"ImageURL"]];

                    }
                    else
                    {
                        cell.coverImageView.image = nil;
                        cell.coverImageView.imageURL=nil;
                    }


                });
            }
        });

    }
    else
    {
        cell.coverImageView.image=[UIImage imageNamed:@"default_cover.png"];
    }
sneha
fuente
Para mí, mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];nunca es nulo, por lo que esto no tiene ningún efecto.
carbocation
1
puede verificar que su celda sea visible o no: for (mycell * updateCell en collectionView.visibleCells) {cellVisible = YES; } if (cellVisible) {cell.coverImageView.imageURL = [NSURL URLWithString: [Dict valueForKey: @ "ImageURL"]]; } También funciona para mí
sneha
@sneha Sí, puedes verificar si es visible iterando de visibleCellsesa manera, pero sospecho que usar [collectionView cellForItemAtIndexPath:indexPath]es más eficiente (y es por eso que haces esa llamada en primer lugar).
Rob
@sneha Además, por cierto, en su ejemplo de código en esta respuesta, más arriba, verifica si updateCellno es así nil, pero luego no lo usa. Debe usarlo no solo para determinar si la celda de la vista de colección aún está visible, sino que debe usarla updateCelldentro de este bloque, no cell(lo que puede que ya no sea válido). Y obviamente, si es así nil, no necesita hacer nada (porque esta celda no es visible).
Rob
2
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
        MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

        cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (data) {
                UIImage *image = [UIImage imageWithData:data];
                if (image) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                        if (updateCell)
                            updateCell.poster.image = image;
                    });
                }
            }
        }];
        [task resume];

        return cell;
    }
Dharmraj Vora
fuente
0

Creo que desea acelerar la carga de su celda en el momento de la carga de la imagen para la celda en segundo plano. Para eso hemos realizado los siguientes pasos:

  1. La comprobación del archivo existe en el directorio del documento o no.

  2. Si no, cargue la imagen por primera vez y guárdela en nuestro directorio de documentos del teléfono. Si no desea guardar la imagen en el teléfono, puede cargar imágenes de celda directamente en el fondo.

  3. Ahora el proceso de carga:

Solo incluye: #import "ManabImageOperations.h"

El código es el siguiente para una celda:

NSString *imagestr=[NSString stringWithFormat:@"http://www.yourlink.com/%@",[dictn objectForKey:@"member_image"]];

        NSString *docDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
        NSLog(@"Doc Dir: %@",docDir);

        NSString  *pngFilePath = [NSString stringWithFormat:@"%@/%@",docDir,[dictn objectForKey:@"member_image"]];

        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pngFilePath];
        if (fileExists)
        {
            [cell1.memberimage setImage:[UIImage imageWithContentsOfFile:pngFilePath] forState:UIControlStateNormal];
        }
        else
        {
            [ManabImageOperations processImageDataWithURLString:imagestr andBlock:^(NSData *imageData)
             {
                 [cell1.memberimage setImage:[[UIImage alloc]initWithData: imageData] forState:UIControlStateNormal];
                [imageData writeToFile:pngFilePath atomically:YES];
             }];
}

ManabImageOperations.h:

#import <Foundation/Foundation.h>

    @interface ManabImageOperations : NSObject
    {
    }
    + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage;
    @end

ManabImageOperations.m:

#import "ManabImageOperations.h"
#import <QuartzCore/QuartzCore.h>
@implementation ManabImageOperations

+ (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage
{
    NSURL *url = [NSURL URLWithString:urlString];

    dispatch_queue_t callerQueue = dispatch_get_main_queue();
    dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL);
    dispatch_async(downloadQueue, ^{
        NSData * imageData = [NSData dataWithContentsOfURL:url];

        dispatch_async(callerQueue, ^{
            processImage(imageData);
        });
    });
  //  downloadQueue=nil;
    dispatch_release(downloadQueue);

}
@end

Verifique la respuesta y comente si se produce algún problema ...

Manab Kumar Mal
fuente
0

Simplemente cambia,

dispatch_async(kBgQueue, ^{
     NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
     dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
     });
 });

Dentro

    dispatch_async(kBgQueue, ^{
         NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
         cell.poster.image = [UIImage imageWithData:imgData];
         dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
         });
     });
Sazzad Hissain Khan
fuente
0

Simplemente puede pasar su URL,

NSURL *url = [NSURL URLWithString:@"http://www.myurl.com/1.png"];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data,    NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (data) {
        UIImage *image = [UIImage imageWithData:data];
        if (image) {
            dispatch_async(dispatch_get_main_queue(), ^{
                    yourimageview.image = image;
            });
        }
    }
}];
[task resume];
Usuario558
fuente
¿puedo saber la razón?
Usuario558
-1
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    Static NSString *CellIdentifier = @"Cell";
    QTStaffViewCell *cell = (QTStaffViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    If (cell == nil)
    {

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"QTStaffViewCell" owner:self options:nil];
        cell = [nib objectAtIndex: 0];

    }

    StaffData = [self.staffArray objectAtIndex:indexPath.row];
    NSString *title = StaffData.title;
    NSString *fName = StaffData.firstname;
    NSString *lName = StaffData.lastname;

    UIFont *FedSanDemi = [UIFont fontWithName:@"Aller" size:18];
    cell.drName.text = [NSString stringWithFormat:@"%@ %@ %@", title,fName,lName];
    [cell.drName setFont:FedSanDemi];

    UIFont *aller = [UIFont fontWithName:@"Aller" size:14];
    cell.drJob.text = StaffData.job;
    [cell.drJob setFont:aller];

    if ([StaffData.title isEqualToString:@"Dr"])
    {
        cell.drJob.frame = CGRectMake(83, 26, 227, 40);
    }
    else
    {
        cell.drJob.frame = CGRectMake(90, 26, 227, 40);

    }

    if ([StaffData.staffPhoto isKindOfClass:[NSString class]])
    {
        NSURL *url = [NSURL URLWithString:StaffData.staffPhoto];
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url
                completionHandler:^(NSURL *location,NSURLResponse *response, NSError *error) {

      NSData *imageData = [NSData dataWithContentsOfURL:location];
      UIImage *image = [UIImage imageWithData:imageData];

      dispatch_sync(dispatch_get_main_queue(),
             ^{
                    cell.imageView.image = image;
              });
    }];
        [task resume];
    }
       return cell;}
Ravindra Kishan
fuente
2
Los volcados de código sin ninguna explicación rara vez son útiles. Por favor considere editar esta respuesta para proporcionar algo de contexto.
Chris