Асинхронная загрузка изображения из URL-адреса внутри ячейки UITableView - при прокрутке изображение изменяется на неправильное

158

Я написал два способа асинхронной загрузки изображений внутри моей ячейки UITableView. В обоих случаях изображение будет загружаться нормально, но когда я прокручиваю таблицу, изображения будут меняться несколько раз, пока прокрутка не закончится и изображение не вернется к нужному изображению. Я понятия не имею, почему это происходит.

#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
источник
5
Вы пытаетесь хранить информацию в реальных ячейках. Это плохо, очень плохо. Вы должны хранить информацию в массиве n (или что-то подобное), а затем отображать ее в ячейках. Информация в этом случае является фактическим UIImage. Да, загрузить его асинхронно, но загрузить в массив.
Fogmeister
1
@Fogmeister Вы имеете в виду poster? Предположительно, это изображение в его пользовательской ячейке, поэтому то, что делает EXEC_BAD_ACCESS, совершенно правильно. Вы правы, что вы не должны использовать ячейку в качестве хранилища для данных модели, но я не думаю, что это то, что он делает. Он просто дает пользовательской ячейке то, что ей нужно представить. Кроме того, и это более тонкая проблема, я бы с осторожностью относился к сохранению самого изображения в массиве вашей модели, поддерживающем ваше табличное представление. Лучше использовать механизм кэширования изображений, и объект вашей модели должен извлекаться из этого кэша.
Роб
1
Да, именно моя точка зрения. Глядя на запрос (который показан полностью), он загружает изображение асинхронно и помещает его непосредственно в imageView в ячейке. (Таким образом, используя ячейку для хранения данных, т.е. изображения). Что он должен делать, так это ссылаться на объект и запрашивать изображение у этого объекта (содержащегося в массиве или где-то еще). Если у объекта еще нет изображения, он должен вернуть заполнитель и загрузить изображение. Затем, когда изображение загружено и готово к отображению, сообщите об этом таблице, чтобы она могла обновить ячейку (если она видна).
Fogmeister
1
То, что он делает, будет вызывать загрузку каждый раз, когда он прокручивает эту ячейку в таблице. Постоянно ли хранятся изображения, зависит от него, но, по крайней мере, храните их в течение всего срока службы таблицы.
Fogmeister
1
Точно: D Таким образом, вам нужно всего лишь извлечь изображение из URL один раз. Вы увидите это на таких вещах, как Facebook Friend Picker. При запуске все аватары являются серыми заполнителями. Затем, когда вы прокручиваете, все они заполняются по мере продвижения. Но затем, когда вы прокрутите назад к ранее показанной ячейке, она мгновенно покажет уже загруженное изображение.
Fogmeister

Ответы:

230

Предполагая, что вы ищете быстрое тактическое исправление, вам нужно убедиться, что изображение ячейки инициализировано, а также что строка ячейки все еще видна, например:

- (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;
}

Приведенный выше код решает несколько проблем, связанных с тем, что ячейка используется повторно:

  1. Вы не инициализируете изображение ячейки перед тем, как инициировать фоновый запрос (это означает, что последнее изображение для извлеченной из ячейки ячейки все еще будет видно, пока загружается новое изображение). Убедитесь, что nilв imageсвойствах любых изображений отображаются, иначе вы увидите мерцание изображений.

  2. Более тонкая проблема заключается в том, что в действительно медленной сети ваш асинхронный запрос может не завершиться, пока ячейка не прокрутится с экрана. Вы можете использовать UITableViewметодcellForRowAtIndexPath: (не путать с одноименным UITableViewDataSourceметодом tableView:cellForRowAtIndexPath:), чтобы увидеть, видна ли еще ячейка для этой строки. Этот метод вернется, nilесли ячейка не видна.

    Проблема заключается в том, что ячейка прокручивалась к тому времени, когда ваш асинхронный метод завершился, и, что еще хуже, ячейка была повторно использована для другой строки таблицы. Проверяя, видна ли строка, вы убедитесь, что случайно не обновили изображение изображением для строки, которая с тех пор прокручивалась за пределы экрана.

  3. В некоторой степени не связанный с рассматриваемым вопросом, я все же был вынужден обновить его, чтобы использовать современные соглашения и API, в частности:

    • Используйте, NSURLSessionа не отправлять-[NSData contentsOfURL:] в фоновую очередь;

    • Используйте dequeueReusableCellWithIdentifier:forIndexPath:вместо dequeueReusableCellWithIdentifier:(но убедитесь, что для этого идентификатора используется прототип ячейки или класс регистра или NIB); и

    • Я использовал имя класса, которое соответствует соглашениям об именах Какао (то есть начинаются с заглавной буквы).

Даже с этими исправлениями есть проблемы:

  1. Приведенный выше код не кэширует загруженные изображения. Это означает, что если вы прокрутите изображение за пределы экрана и вернетесь на экран, приложение может попытаться получить изображение снова. Возможно, вам повезет, что ваши заголовки ответа сервера позволят довольно прозрачное кэширование, предлагаемое NSURLSessionиNSURLCache , но если нет, то вы будете делать ненужные запросы к серверу и предлагая гораздо медленнее UX.

  2. Мы не отменяем запросы на ячейки, которые прокручиваются за пределы экрана. Таким образом, если вы быстро прокрутите до 100-й строки, изображение для этой строки может быть перенесено за запросами для предыдущих 99 строк, которые даже больше не видны. Вы всегда хотите убедиться, что вы расставляете приоритеты запросов на видимые ячейки для лучшего UX.

Простейшее решение этих проблем - использование UIImageViewкатегории, например, предоставляемой SDWebImage или AFNetworking . Если вы хотите, вы можете написать свой собственный код для решения вышеуказанных проблем, но это много работы, и вышеупомянутые UIImageViewкатегории уже сделали это для вас.

обкрадывать
источник
1
Спасибо. Я считаю, что вам нужно отредактировать свой ответ. updateCell.poster.image = nilto cell.poster.image = nil;updateCell вызывается до того, как объявлено.
Сегев
1
Мое приложение использует много JSON, так что AFNetworkingэто определенно путь. Я знал об этом, но был ленив, чтобы использовать это. Я просто восхищаюсь тем, как кеширование работает с их простой строкой кода. [imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
Сегев
2
Пробовал все вышеперечисленное и SDWebImage (на самом деле остановился здесь и даже не нужно было пытаться AFNetworking), и это был безусловно лучший выбор. Спасибо @Rob.
mondousage
1
Когда вы закончите загрузку изображения и обновите представление изображения, удалите индикатор активности. Единственная хитрость заключается в том, что вы должны предвидеть, что произойдет, если ячейка прокручивается из поля зрения, когда изображение извлекается, и ячейка используется повторно для другой строки, вам придется обнаруживать наличие любого существующего индикатора активности и удалять / обновлять это не просто предположить, что ячейка свободна от существующего индикатора.
Роб
1
Первоначальный вопрос был «почему это cellForRowAtIndexPathприводит к мерцанию изображений при быстрой прокрутке», и я объяснил, почему это произошло, а также как это исправить. Но я продолжил объяснять, почему даже этого было недостаточно, описал несколько более глубоких проблем и спорил, почему вам лучше использовать одну из этих библиотек для более изящной обработки (расставьте приоритеты для запросов на видимые ячейки, кэшируйте, чтобы избежать избыточной сети). запросы и т. д.). Мне неясно, что еще вы ожидали в ответ на вопрос «как мне остановить мерцающие изображения в моем табличном представлении».
Роб
15

/ * Я сделал это таким образом, а также проверил это * /

Шаг 1 = Зарегистрируйте пользовательский класс ячейки (в случае ячейки прототипа в таблице) или nib (в случае настраиваемого пера для настраиваемой ячейки) для таблицы, как это в методе viewDidLoad:

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

ИЛИ

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

Шаг 2 = Используйте метод UITableView «dequeueReusableCellWithIdentifier: forIndexPath:», как этот (для этого вы должны зарегистрировать класс или nib):

   - (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;
        }
Нитеш Борад
источник
1
Разве при вызове cellForRowAtIndexPath в последнем блоке все это происходит во второй раз?
Марк Бриджес
@MarkBridges, Нет. На самом деле я вызываю метод cellForRowAtIndexPath tableView здесь. Не путайте с источником данных tableView с тем же именем. Требуется, его можно вызвать как [self tableView: tableView cellForRowAtIndexPath: indexPath]; Надеюсь, это очистит ваше замешательство.
Нитеш Борад
14

Есть несколько платформ, которые решают эту проблему. Просто назвать несколько:

Swift:

Objective-C:

Кина
источник
Пожалуйста, добавьте ваши предложения, если есть другие рамки, которые стоит рассмотреть.
Кен
3
На самом деле SDWebImageне решает эту проблему. Вы можете контролировать, когда изображение загружается, но SDWebImageназначить изображение, UIImageViewне спрашивая у вас разрешения на это. В принципе, проблема из вопроса до сих пор не решена с этой библиотекой.
Bartłomiej Semańczyk
Проблема этого вопроса заключалась в том, что автор не проверял, использовалась ли ячейка повторно или нет. Это очень основная проблема, которая решается этими платформами, включая SDWebImage.
Кин
SDWebImage очень запаздывает с iOS 8, это была одна из моих любимых фреймворков, но сейчас я начинаю использовать PinRemoteImage, который работает очень хорошо.
Джоан Кардона
@ BartłomiejSemańczyk Вы правы, эта проблема не решена с помощью SDWebimage
января
9

Свифт 3

Я пишу свою собственную легкую реализацию для загрузчика изображений с использованием NSCache. Нет мерцания изображения клетки!

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()
        }
    }
}

Пример использования

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
}
Дмитрий Класснецкий
источник
3
я бы сказал спасибо тебе. но код имеет небольшую проблему. если позволить data = попробовать? Data (contentsOf: url) {// пожалуйста, замените URL на местоположение. это помогло бы многим людям.
Карл Хунг
2
С кодом как есть, вы загружаете файл дважды по сети: один раз в downloadTaks, один раз с данными (cntentsOf :). Вы должны указать местоположение пользователя вместо URL, потому что задача загрузки просто загружает по сети, записывает данные во временный файл и передает вам localUrl (местоположение в вашем случае). Таким образом, данные должны указывать на локальный URL-адрес, чтобы он читал только из файла.
Стефан де Лука
В примере использования это должно быть "ImageCacheLoader.obtainImageWithPath (imagePath: viewModel.image) ......."?
Тим Крюгер
не будет работать при очень быстрой прокрутке, изображения будут поменяться много раз из-за повторного использования ячейки.
Хуан Боэро
5

Вот быстрая версия (с использованием @Nitesh Borad target C code): -

   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()
            }
Чатуранга Силва
источник
3

Лучший ответ - неправильный способ сделать это :(. Вы на самом деле связали indexPath с моделью, что не всегда хорошо. Представьте, что некоторые строки были добавлены во время загрузки изображения. Теперь ячейка для данного indexPath существует на экране, но изображение уже не правильно! Ситуация довольно маловероятна и ее трудно воспроизвести, но это возможно.

Лучше использовать подход MVVM, связать ячейку с viewModel в контроллере и загрузить изображение в viewModel (назначение сигнала ReactiveCocoa методом switchToLatest), затем подписать этот сигнал и назначить изображение ячейке! ;)

Вы должны помнить, чтобы не злоупотреблять MVVM. Взгляды должны быть до смерти просты! Принимая во внимание, что ViewModels должен быть многоразовым! Вот почему очень важно связать View (UITableViewCell) и ViewModel в контроллере.

badeleux
источник
1
Да, мой путь к указателю «тактическое исправление» (который я не рекомендовал, а скорее был просто самым скромным редактированием для решения проблемы OP) страдает от этой проблемы (но только если в табличном представлении продолжают добавляться / удаляться строки). И если бы это явление проявилось, я мог бы исправить это другими способами (вместо того, чтобы искать, используя тот же путь индекса, просто модель запроса для соответствующей строки). Но у этого тактического исправления есть даже более вопиющие проблемы (которые я опишу выше), чем та, которую вы подняли здесь. Если вы используете UIImageViewсовет по категориям, который я советую, то нет такой проблемы в отношении путей индекса.
Роб
2
Я могу звучать немного педантично, но использование любой логики из VIEW злоупотребляет этой архитектурой.
Badeleux
3

В моем случае это было не из-за кэширования изображений (используется SDWebImage). Это было из-за несоответствия тега пользовательской ячейки с indexPath.row.

На cellForRowAtIndexPath:

1) Назначьте значение индекса для вашей пользовательской ячейки. Например,

cell.tag = indexPath.row

2) В главном потоке перед назначением изображения проверьте, принадлежит ли изображение соответствующей ячейке, сопоставив его с тегом.

dispatch_async(dispatch_get_main_queue(), ^{
   if(cell.tag == indexPath.row) {
     UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
     thumbnailImageView.image = tmpImage;
   }});
});
AG
источник
2

Спасибо "Роб" .... У меня была такая же проблема с UICollectionView, и ваш ответ помог мне решить мою проблему. Вот мой код:

 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"];
    }
снеха
источник
Для меня mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];это никогда не ноль, так что это не имеет никакого эффекта.
карбокатион
1
Вы можете проверить, видна или нет ваша ячейка с помощью: for (mycell * updateCell in collectionView.visibleCells) {cellVisible = YES; } if (cellVisible) {cell.coverImageView.imageURL = [NSURL URLWithString: [Dict valueForKey: @ "ImageURL"]]; } Это работает и для меня
sneha
@sneha Да, вы можете проверить, чтобы увидеть его, повторяя visibleCellsподобное, но я подозреваю, что использование [collectionView cellForItemAtIndexPath:indexPath]более эффективно (и именно поэтому вы делаете этот вызов в первую очередь).
Роб
@sneha Кроме того, кстати, в приведенном выше примере кода в этом ответе вы проверяете, если updateCellнет nil, но затем не используете его. Вы должны использовать его не только для определения, является ли ячейка представления коллекции все еще видимой, но затем вы должны использовать updateCellвнутри этого блока, а не cell(который может быть недействительным). И, очевидно, если это так nil, вам не нужно ничего делать (потому что эта ячейка не видна).
Роб
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;
    }
Дхармрай Вора
источник
0

Я думаю, что вы хотите ускорить загрузку вашей ячейки во время загрузки изображения для ячейки в фоновом режиме. Для этого мы сделали следующие шаги:

  1. Проверка наличия файла в каталоге документов или нет.

  2. Если нет, тогда загрузите изображение в первый раз и сохраните его в каталоге документов нашего телефона. Если вы не хотите сохранять изображение в телефоне, вы можете загружать изображения в ячейку прямо на фон.

  3. Теперь процесс загрузки:

Просто включите: #import "ManabImageOperations.h"

Код, как показано ниже для ячейки:

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

Пожалуйста, проверьте ответ и прокомментируйте, если возникает какая-либо проблема ....

Манаб Кумар Мал
источник
0

Просто поменяй,

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];
     });
 });

В

    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];
         });
     });
Саззад Хисейн Хан
источник
0

Вы можете просто передать свой 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];
User558
источник
могу я узнать причину?
User558
-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;}
Равиндра Кишан
источник
2
Дампы кода без каких-либо объяснений редко бывают полезными. Пожалуйста, рассмотрите возможность редактирования этого ответа, чтобы обеспечить некоторый контекст.
Крис