Async memuat gambar dari url di dalam sel UITableView - gambar berubah menjadi gambar yang salah saat menggulir


158

Saya telah menulis dua cara untuk memuat gambar di dalam sel UITableView saya. Dalam kedua kasus gambar akan memuat dengan baik tetapi ketika saya akan menggulir tabel gambar akan berubah beberapa kali sampai gulir akan berakhir dan gambar akan kembali ke gambar yang tepat. Saya tidak tahu mengapa ini terjadi.

#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
Anda mencoba menyimpan informasi di sel yang sebenarnya. Ini buruk, sangat buruk. Anda harus menyimpan informasi dalam n array (atau yang serupa) dan kemudian menampilkannya di sel. Informasi dalam hal ini adalah UIImage yang sebenarnya. Ya muat secara tidak sinkron, tetapi muat ke dalam array.
Fogmeister

1
@Fogmeister Anda maksudkan poster? Mungkin itu adalah tampilan gambar di sel khususnya, jadi apa yang dilakukan EXEC_BAD_ACCESS benar. Anda benar bahwa Anda tidak boleh menggunakan sel sebagai repositori untuk data model, tapi saya tidak berpikir itu yang dia lakukan. Dia hanya memberi sel kustom apa yang dibutuhkan untuk menampilkan dirinya sendiri. Selanjutnya, dan ini adalah masalah yang lebih halus, saya akan berhati-hati tentang menyimpan gambar, itu sendiri, dalam model array Anda mendukung tampilan tabel Anda. Lebih baik menggunakan mekanisme caching gambar dan objek model Anda harus mengambil dari cache itu.
Rob

1
Ya, tepatnya maksud saya. Melihat permintaan (yang ditunjukkan secara lengkap) ia mengunduh gambar secara tidak sinkron dan memasukkannya langsung ke imageView di dalam sel. (Dengan demikian menggunakan sel untuk menyimpan data, yaitu gambar). Apa yang harus dia lakukan adalah mereferensikan objek dan meminta gambar dari objek tersebut (terkandung dalam array atau di suatu tempat). Jika objek belum memiliki gambar itu harus mengembalikan placeholder dan mengunduh gambar. Kemudian ketika gambar diunduh dan siap untuk ditampilkan, beri tahu tabel sehingga dapat memperbarui sel (jika terlihat).
Fogmeister

1
Apa yang dia lakukan akan memaksa unduhan setiap kali dia menggulir ke sel itu di tabel. Apakah gambar disimpan secara terus-menerus adalah terserah dia, tetapi setidaknya menyimpannya untuk seumur hidup tampilan tabel.
Fogmeister

1
Tepat: D Dengan begitu Anda hanya perlu mengambil gambar dari URL satu kali. Anda akan melihat ini pada hal-hal seperti Facebook Friend Picker. Saat Anda memulai, semua avatar adalah placeholder abu-abu. Kemudian saat Anda menggulir, mereka semua mengisi saat bergerak. Tetapi ketika Anda menggulir kembali ke sel yang sebelumnya ditampilkan, akan langsung menampilkan gambar yang sudah diunduh.
Fogmeister

Jawaban:


230

Dengan asumsi Anda sedang mencari perbaikan taktis cepat, apa yang perlu Anda lakukan adalah memastikan gambar sel diinisialisasi dan juga bahwa baris sel masih terlihat, misalnya:

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

Kode di atas mengatasi beberapa masalah yang berasal dari fakta bahwa sel digunakan kembali:

  1. Anda tidak menginisialisasi gambar sel sebelum memulai permintaan latar belakang (artinya gambar terakhir untuk sel yang dikosongkan masih akan terlihat saat gambar baru sedang diunduh). Pastikan untuk nilmelihat imageproperti gambar atau Anda akan melihat kerlipan gambar.

  2. Masalah yang lebih halus adalah bahwa pada jaringan yang sangat lambat, permintaan asinkron Anda mungkin tidak selesai sebelum sel bergulir dari layar. Anda dapat menggunakan UITableViewmetode ini cellForRowAtIndexPath:(jangan bingung dengan UITableViewDataSourcemetode yang disebut sama tableView:cellForRowAtIndexPath:) untuk melihat apakah sel untuk baris itu masih terlihat. Metode ini akan kembali niljika sel tidak terlihat.

    Masalahnya adalah bahwa sel telah digulirkan pada saat metode async Anda telah selesai, dan, lebih buruk lagi, sel telah digunakan kembali untuk baris lain dari tabel. Dengan memeriksa untuk melihat apakah baris masih terlihat, Anda akan memastikan bahwa Anda tidak secara tidak sengaja memperbarui gambar dengan gambar untuk baris yang sejak saat itu bergulir dari layar.

  3. Agak tidak terkait dengan pertanyaan yang ada, saya masih merasa terdorong untuk memperbarui ini untuk meningkatkan konvensi dan API modern, terutama:

    • Gunakan NSURLSessiondaripada mengirim -[NSData contentsOfURL:]ke antrian latar belakang;

    • Gunakan dequeueReusableCellWithIdentifier:forIndexPath:daripada dequeueReusableCellWithIdentifier:(tetapi pastikan untuk menggunakan prototipe sel atau kelas register atau NIB untuk pengidentifikasi itu); dan

    • Saya menggunakan nama kelas yang sesuai dengan konvensi penamaan Kakao (yaitu mulai dengan huruf besar).

Bahkan dengan koreksi ini, ada masalah:

  1. Kode di atas bukanlah caching gambar yang diunduh. Itu berarti bahwa jika Anda menggulir gambar dari layar dan kembali ke layar, aplikasi mungkin mencoba untuk mengambil gambar lagi. Mungkin Anda akan cukup beruntung bahwa header respons server Anda akan mengizinkan caching yang cukup transparan yang ditawarkan oleh NSURLSessiondan NSURLCache, tetapi jika tidak, Anda akan membuat permintaan server yang tidak perlu dan menawarkan UX yang jauh lebih lambat.

  2. Kami tidak membatalkan permintaan sel yang menggulir layar. Jadi, jika Anda dengan cepat menggulir ke baris ke-100, gambar untuk baris itu bisa ditumpuk di belakang permintaan 99 baris sebelumnya yang bahkan tidak terlihat lagi. Anda selalu ingin memastikan Anda memprioritaskan permintaan sel yang terlihat untuk UX terbaik.

Perbaikan paling sederhana yang mengatasi masalah ini adalah dengan menggunakan UIImageViewkategori, seperti yang disediakan dengan SDWebImage atau AFNetworking . Jika mau, Anda dapat menulis kode sendiri untuk mengatasi masalah di atas, tetapi ini banyak pekerjaan, dan UIImageViewkategori di atas sudah melakukan ini untuk Anda.


1
Terima kasih. Saya yakin Anda perlu mengedit jawaban Anda. updateCell.poster.image = nilto cell.poster.image = nil;updateCell dipanggil sebelum diumumkan.
Segev

1
Aplikasi saya menggunakan banyak json jadi AFNetworkingpasti cara untuk pergi. Saya tahu tentang itu tetapi malas menggunakannya. Saya hanya mengagumi cara cache bekerja dengan garis kode sederhana mereka. [imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
Segev

2
Mencoba semua hal di atas dan SDWebImage (sebenarnya berhenti di sini dan bahkan tidak perlu mencoba AFNetworking) dan sejauh ini merupakan pilihan terbaik. Terima kasih @Rob.
mondousage

1
Ketika Anda selesai memuat gambar dan memperbarui tampilan gambar, hapus indikator aktivitas. Satu-satunya trik adalah Anda harus mengantisipasi apa yang terjadi jika sel tidak terlihat ketika gambar diambil dan sel digunakan kembali untuk baris lain, Anda harus mendeteksi keberadaan setiap indikator aktivitas yang ada dan menghapus / memperbarui itu, tidak hanya menganggap sel bebas dari indikator yang ada.
Rob

1
Pertanyaan aslinya adalah "mengapa ini cellForRowAtIndexPathmenghasilkan kerlip gambar ketika saya gulir dengan cepat" dan saya menjelaskan mengapa itu terjadi serta bagaimana cara memperbaikinya. Tetapi saya terus menjelaskan mengapa bahkan itu tidak cukup, menggambarkan beberapa masalah yang lebih dalam, dan berpendapat mengapa Anda akan lebih baik menggunakan salah satu perpustakaan itu untuk menangani hal ini dengan lebih anggun (memprioritaskan permintaan untuk sel yang terlihat, caching untuk menghindari jaringan yang berlebihan. permintaan, dll.). Saya tidak jelas apa lagi yang Anda harapkan dalam menanggapi pertanyaan "bagaimana cara menghentikan gambar yang berkedip di tampilan tabel saya".
Rob

15

/ * Saya telah melakukannya dengan cara ini, dan juga mengujinya * /

Langkah 1 = Daftarkan kelas sel khusus (dalam kasus sel prototipe dalam tabel) atau nib (dalam kasus nib khusus untuk sel khusus) untuk tabel seperti ini dalam metode viewDidLoad:

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

ATAU

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

Langkah 2 = Gunakan metode "dequeueReusableCellWithIdentifier UITableView: forIndexPath:" seperti ini (untuk ini, Anda harus mendaftar kelas atau 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
Tidak memanggil cellForRowAtIndexPath dalam blok terakhir menyebabkan semuanya dipecat untuk kedua kalinya?
Mark Bridges

@MarkBridges, Tidak. Sebenarnya saya memanggil metode cellForRowAtIndexPath tableView di sini. Jangan bingung dengan metode sumber data tableView dengan nama yang sama. Diperlukan, ini bisa disebut seperti [self tableView: tableView cellForRowAtIndexPath: indexPath]; Semoga ini akan menghapus kebingungan Anda.
Nitesh Borad

14

Ada beberapa kerangka kerja yang mengatasi masalah ini. Hanya untuk beberapa nama:

Cepat:

Tujuan-C:


Harap tambahkan saran Anda jika ada kerangka kerja lain yang perlu dipertimbangkan.
kean

3
Sebenarnya SDWebImagetidak menyelesaikan masalah ini. Anda dapat mengontrol kapan gambar diunduh, tetapi SDWebImagemenetapkan gambar UIImageViewtanpa meminta Anda tentang izin untuk melakukan ini. Pada dasarnya, masalah dari pertanyaan tersebut masih belum diselesaikan dengan perpustakaan ini.
Bartłomiej Semańczyk

Masalah dari pertanyaan adalah bahwa penulis tidak memeriksa apakah sel itu digunakan kembali atau tidak. Ini adalah masalah yang sangat mendasar yang ditangani oleh kerangka kerja tersebut, termasuk SDWebImage.
kean

SDWebImage sangat lamban sejak iOS 8, itu adalah salah satu framework favorit saya, tetapi sekarang saya mulai menggunakan PinRemoteImage yang bekerja dengan sangat baik.
Joan Cardona

@ BartłomiejSemańczyk Anda benar, masalah ini tidak diselesaikan oleh SDWebimage
Jan

9

Cepat 3

Saya menulis implementasi cahaya saya sendiri untuk pemuat gambar dengan menggunakan NSCache. Tidak ada gambar sel yang berkedip!

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

Contoh penggunaan

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
aku akan mengucapkan terima kasih padamu. tetapi kode memiliki sedikit masalah. jika biarkan data = coba? Data (contentOf: url) {// harap ganti url ke lokasi. itu akan membantu banyak orang.
Carl Hung

2
Dengan kode apa adanya, Anda mengunduh dua kali file melalui jaringan: sekali di dalam downloadTaks, sekali dengan Data (cntentsOf :). Anda harus lokasi pengguna sebagai ganti url karena tugas unduhan sebenarnya hanya mengunduh melalui jaringan dan menulis data ke file sementara dan memberikan Anda localUrl (lokasi dalam kasus Anda). Jadi, Data perlu mengarah ke Url lokal sehingga hanya membaca dari file.
Stéphane de Luca

Dalam Contoh Penggunaan, apakah ini seharusnya "ImageCacheLoader.obtainImageWithPath (imagePath: viewModel.image) ......."?
Tim Kruger

tidak akan berfungsi ketika menggulir sangat cepat, gambar akan bertukar berkali-kali karena penggunaan kembali sel.
Juan Boero

5

Ini adalah versi cepat (dengan menggunakan kode obyektif C borad @Nitesh): -

   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

Jawaban terbaik bukan cara yang benar untuk melakukan ini :(. Anda benar-benar terikat indexPath dengan model, yang tidak selalu baik. Bayangkan bahwa beberapa baris telah ditambahkan selama memuat gambar. Sekarang sel untuk indexPath yang diberikan ada di layar, tetapi gambar tidak lagi benar! Situasinya agak tidak mungkin dan sulit untuk ditiru tetapi itu mungkin.

Lebih baik menggunakan pendekatan MVVM, mengikat sel dengan viewModel di controller dan memuat gambar di viewModel (menetapkan sinyal ReactiveCocoa dengan metode switchToLatest), kemudian berlangganan sinyal ini dan tetapkan gambar ke sel! ;)

Anda harus ingat untuk tidak menyalahgunakan MVVM. Tampilan harus mati sederhana! Sedangkan ViewModels harus dapat digunakan kembali! Itu sebabnya sangat penting untuk mengikat View (UITableViewCell) dan ViewModel di controller.


1
Ya, jalur indeks saya "perbaikan taktis" (yang saya tidak rekomendasikan, tetapi hanya mengedit yang paling sederhana untuk menyelesaikan masalah OP) menderita masalah ini (tetapi hanya jika tampilan tabel terus memiliki baris ditambahkan / dihapus). Dan jika fenomena ini memanifestasikan dirinya, saya mungkin memperbaikinya dengan cara lain (daripada mencari menggunakan jalur indeks yang sama, hanya model permintaan untuk baris yang sesuai). Tetapi perbaikan taktis itu bahkan memiliki masalah yang lebih mengerikan (yang saya jelaskan di atas) daripada yang Anda ajukan di sini. Jika Anda menggunakan UIImageViewsolusi kategori yang saya sarankan, maka tidak ada masalah tentang jalur indeks.
Rob

2
Saya mungkin terdengar sedikit terlalu berlebihan, tetapi memohon segala jenis logika dari VIEW menyalahgunakan arsitektur ini.
badeleux

3

Dalam kasus saya, itu bukan karena caching gambar (SDWebImage Digunakan). Itu karena ketidakcocokan tag sel kustom dengan indexPath.row.

Di cellForRowAtIndexPath:

1) Tetapkan nilai indeks ke sel khusus Anda. Misalnya,

cell.tag = indexPath.row

2) Pada utas utama, sebelum menetapkan gambar, periksa apakah gambar itu milik sel yang sesuai dengan mencocokkannya dengan tag.

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

2

Terima kasih "Rob" .... Saya memiliki masalah yang sama dengan UICollectionView dan jawaban Anda membantu saya untuk menyelesaikan masalah saya. Ini kode saya:

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

Bagi saya, mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];tidak pernah nol, jadi ini tidak berpengaruh.
carbocation

1
Anda dapat memeriksa apakah sel Anda terlihat atau tidak oleh: for (mycell * updateCell in collectionView.visibleCells) {cellVisible = YES; } if (cellVisible) {cell.coverImageView.imageURL = [NSURL URLWithString: [Dict valueForKey: @ "ImageURL"]]; } Ini juga bekerja untuk saya
sneha

@sneha Yap, Anda dapat memeriksa untuk melihat itu terlihat dengan mengulangi visibleCellsseperti itu, tapi saya menduga menggunakan [collectionView cellForItemAtIndexPath:indexPath]lebih efisien (dan itulah sebabnya Anda melakukan panggilan itu di tempat pertama).
Rob

@ sneha Juga, omong-omong, dalam sampel kode Anda dalam jawaban ini, di atas, Anda memeriksa untuk melihat apakah updateCelltidak nil, tetapi Anda kemudian tidak menggunakannya. Anda harus menggunakannya tidak hanya untuk menentukan apakah sel tampilan kumpulan masih terlihat, tetapi Anda kemudian harus menggunakan updateCelldi dalam blok ini, tidak cell(yang mungkin tidak berlaku lagi). Dan jelas, jika itu nil, Anda tidak perlu melakukan apa-apa (karena sel ini tidak terlihat).
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;
    }

0

Saya pikir Anda ingin mempercepat pemuatan sel Anda pada saat pemuatan gambar untuk sel di latar belakang. Untuk itu kami telah melakukan langkah-langkah berikut:

  1. Memeriksa file yang ada di direktori dokumen atau tidak.

  2. Jika tidak memuat gambar untuk pertama kalinya, dan menyimpannya ke direktori dokumen telepon kami. Jika Anda tidak ingin menyimpan gambar di telepon maka Anda dapat memuat gambar sel secara langsung di latar belakang.

  3. Sekarang proses memuat:

Cukup sertakan: #import "ManabImageOperations.h"

Kode seperti di bawah ini untuk sel:

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

Silakan periksa jawaban dan komentar jika ada masalah ....


0

Cukup ubah,

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

Ke

    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

Anda bisa meneruskan URL Anda,

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

bolehkah saya tahu alasannya?
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
Kesedihan kode tanpa penjelasan apa pun jarang membantu. Silakan pertimbangkan untuk mengedit jawaban ini untuk memberikan beberapa konteks.
Chris
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.