Асинхронізувати завантаження зображення з 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;
}

5
Ви намагаєтесь зберігати інформацію у фактичних клітинках. Це погано, дуже погано. Ви повинні зберігати інформацію в n масиві (або щось подібне), а потім відображати її в клітинках. Інформація в цьому випадку є фактичним UIImage. Так, завантажте його асинхронно, але завантажте в масив.
Фогмайстер

1
@Fogmeister Ви посилаєтесь poster? Це, мабуть, перегляд зображень у його користувальницькій комірці, тому те, що робить EXEC_BAD_ACCESS, є абсолютно правильним. Ви вірні, що не слід використовувати комірку як сховище для даних моделі, але я не думаю, що це він робить. Він просто дає користувальницькій клітинці те, що їй потрібно для представлення. Крім того, і це більш тонка проблема, я б насторожено ставився до зберігання зображення у вашому масиві моделей, що підтримує ваш перегляд таблиці. Краще використовувати механізм кешування зображень, і ваш об'єкт моделі повинен отримати з цього кеша.
Роб

1
Так, саме моя думка. Дивлячись на запит (який показаний повністю), він завантажує зображення асинхронно і вводить його безпосередньо в imageView у комірці. (Таким чином, використовуючи комірку для зберігання даних, тобто зображення). Що він повинен робити - це посилання на об'єкт і запит зображення у цього об’єкта (що міститься в масиві чи десь). Якщо об'єкт ще не має зображення, він повинен повернути заповнювач і завантажити його. Потім, коли зображення завантажується і готове до відображення, повідомте таблицю, щоб вона могла оновити клітинку (якщо її видно).
Фогмайстер

1
Те, що він робить, змушує завантажувати щоразу, коли він прокручує цю клітинку таблиці. Чи постійно зберігаються зображення, залежить від нього, але принаймні зберігати їх протягом життя таблиці.
Фогмайстер

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 = nilдо 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 = Використовуйте метод "dequeueReusableCellWithIdentifier: forIndexPath:" типу UITableView таким чином (для цього потрібно зареєструвати клас або ниб):

   - (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, Ні. Насправді тут я закликаю метод cellView CellForRowAtIndexPath. Не плутайте з тим же методом джерела даних tableView. Це потрібно, його можна назвати як [self tableView: tableView cellForRowAtIndexPath: indexPath]; Сподіваємось, це очистить вашу плутанину.
Нітеш Борад

14

Існує декілька рамок, які вирішують цю проблему. Просто для назви декількох:

Швидкий:

Завдання-C:


Будь ласка, додайте свої пропозиції, якщо є інші рамки, які варто розглянути.
kean

3
Насправді SDWebImageце питання не вирішує. Ви можете контролювати завантаження зображення, але SDWebImageпризначте його, UIImageViewне запитуючи про дозвіл на це. В основному, проблема з питання досі не вирішена з цією бібліотекою.
Bartłomiej Semańczyk

Проблема з питання полягала в тому, що автор не перевіряв, чи використовували клітинку повторно чи ні. Це дуже основна проблема, яку вирішують ті рамки, включаючи SDWebImage.
kean

SDWebImage дуже млявий з iOS 8, це була одна з моїх улюблених рамок, але зараз я починаю використовувати PinRemoteImage, який працює дуже добре.
Джоан Кардона

@ BartłomiejSemańczyk Ви маєте рацію, ця проблема вирішується оленяча шкіра SDWebimage
Jan

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
я б сказав спасибі вам. але код має трохи проблем. якщо нехай дані = спробуйте? Дані (contentOf: url) {// замініть URL-адресу на адресу. це допомогло б багатьом людям.
Карл Хун

2
З таким кодом ви двічі завантажуєте файл по мережі: один раз у downloadTaks, один раз із даними (cntentsOf :). Ви повинні розташувати користувача замість URL-адреси, оскільки завдання завантаження просто фактично завантажується по мережі та записує дані у тимчасовий файл і передає вам localUrl (місце у вашому випадку). Отже, Дані потрібно вказувати на локальну URL-адресу, щоб вона читалася лише з файлу.
Стефан де Лука

Чи слід в прикладі використання бути "ImageCacheLoader.obtainImageWithPath (imagePath: viewModel.image) ......."?
Тім Крюгер

не буде працювати, коли прокручувати дуже швидко, зображення будуть мінятися багато разів через повторне використання комірок.
Хуан Боеро

5

Ось швидка версія (використовуючи код об'єкта C @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()
            }

3

Найкраща відповідь - не правильний спосіб зробити це :(. Ви насправді зв'язали indexPath з моделлю, що не завжди добре. Уявіть, що під час завантаження зображення додано кілька рядків. Тепер на екрані існує клітинка для даного indexPath, але зображення більше не виправдано! Ситуація начебто малоймовірна і складна для копіювання, але це можливо.

Краще скористатися підходом MVVM, зв’язати комірку з viewModel в контролері та завантажити зображення в viewModel (призначення сигналу ReactiveCocoa методом switchToLatest), після чого підписати цей сигнал і призначити зображення клітинці! ;)

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


1
Так, моя проблема "тактичне виправлення" індексу (яку я не рекомендував, але, скоріше, була найскромнішою редакцією для вирішення проблеми ОП), страждає від цього питання (але лише в тому випадку, якщо в табличному перегляді продовжуються додавання / видалення рядків). І якби це явище проявилося, я можу виправити інші способи (замість того, щоб шукати за допомогою того самого шляху індексу, просто запитуйте модель для відповідного рядка). Але це тактичне виправлення має навіть більш грізні проблеми (які я описав вище), ніж ті, які ви тут піднімаєте. Якщо ви використовуєте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;
   }});
});

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
ви можете перевірити, чи ваша клітинка є видимою чи ні: за (mycell * updateCell у collectionView.visibleCells) {cellVisible = ТАК; } 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];

чи можу я знати причину?
Користувач558,

-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
Скидання коду без будь-якого пояснення дуже рідко корисні. Будь ласка, подумайте про редагування цієї відповіді, щоб дати певний контекст.
Кріс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.