UITableView hücresi içindeki URL'den zaman uyumsuz resim yükleme - kaydırma sırasında resim yanlış resme dönüşüyor


158

UITableView hücresimin içine resim yüklemek için iki yolunu yazmanın iki yolunu yazdım. Her iki durumda da görüntü iyi yüklenir, ancak tabloyu kaydırdığımda, kaydırma sona erene ve görüntü sağ görüntüye geri dönene kadar görüntüler birkaç kez değişecektir. Bunun neden olduğu hakkında hiçbir fikrim yok.

#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
Bilgileri gerçek hücrelere depolamaya çalışıyorsunuz. Bu kötü, çok kötü. Bilgileri n dizisinde (veya benzer bir şeyde) saklamalı ve sonra hücrelerde görüntülemelisiniz. Bu durumda bilgiler gerçek UIImage'dır. Evet, eşzamansız olarak yükleyin, ancak bir diziye yükleyin.
Fogmeister

1
@Fogmeister Eğer kastediyorsun poster? Bu muhtemelen kendi özel hücresinde bir hayalgücüdür, bu nedenle EXEC_BAD_ACCESS'in yaptığı tam olarak doğrudur. Hücreyi model verileri için depo olarak kullanmamanız konusunda haklısınız, ancak onun yaptığı şeyin bu olduğunu düşünmüyorum. Sadece özel hücreye kendini sunmak için ihtiyaç duyduğu şeyi veriyor. Dahası, ve bu daha ince bir konudur, tablo görüntünüzü destekleyen model dizinizde bir görüntünün kendisini saklamak konusunda dikkatli olurum. Bir görüntü önbellek mekanizması kullanmak daha iyidir ve model nesneniz bu önbellekten almalıdır.
Rob

1
Evet, tam olarak benim açımdan. (Tam olarak gösterilen) isteğe bakarak görüntüyü eşzamansız olarak indiriyor ve doğrudan hücredeki imageView'e koyuyor. (Böylece veri, yani görüntü depolamak için hücre kullanarak). Yapması gereken bir nesneye referans vermek ve o nesneden (bir dizide veya bir yerde bulunan) görüntü istemektir. Nesne henüz görüntüye sahip değilse, bir yer tutucu döndürmeli ve görüntüyü indirmelidir. Daha sonra görüntü indirilip görüntülenmeye hazır olduğunda, tabloyu hücreyi güncelleyebilmesi için (görünürse) bildirin.
Fogmeister

1
Yaptığı şey indirmeyi tablodaki o hücreye her kaydırdığında zorlayacaktır. Görüntülerin kalıcı olarak saklanıp saklanmadığı ona bağlıdır, ancak en azından tablo görüntüsünün ömrü boyunca saklayın.
Fogmeister

1
Tam olarak: D Bu şekilde, resmi URL'den yalnızca bir kez getirmeniz gerekir. Bunu Facebook Friend Picker gibi şeylerde göreceksiniz. Başladığınızda tüm avatarlar gri yer tutuculardır. Sonra kaydırdıkça hareket ettikçe hepsi doldurulur. Ancak daha önce gösterilen bir hücreye geri döndüğünüzde, önceden indirilmiş görüntüyü anında gösterir.
Fogmeister

Yanıtlar:


230

Hızlı bir taktik düzeltme aradığınızı varsayarsak, yapmanız gereken hücre görüntüsünün başlatıldığından ve ayrıca hücrenin satırının hala görünür olduğundan emin olmaktır, örneğin:

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

Yukarıdaki kod, hücrenin yeniden kullanıldığı gerçeğinden kaynaklanan birkaç sorunu ele almaktadır:

  1. Arka plan isteğini başlatmadan önce hücre görüntüsünü başlatmıyorsunuz (yani, yeni görüntü indirilirken, kaldırılmış hücre için son görüntünün görünmeye devam edeceği anlamına gelir). Emin olun herhangi bir görüntü görüşlerin mülkiyet veya başka görüntülerin titremeyi göreceksiniz.nilimage

  2. Daha ince bir sorun, gerçekten yavaş bir ağda, hücre ekrandan kaymadan önce eşzamansız isteğinizin bitmeyebilmesidir. Bu satırın hücresinin hala görünüp görünmediğini görmek için UITableViewyöntemi cellForRowAtIndexPath:(benzer şekilde adlandırılmış UITableViewDataSourceyöntemle karıştırılmamalıdır tableView:cellForRowAtIndexPath:) kullanabilirsiniz. nilHücre görünmüyorsa bu yöntem geri döner .

    Sorun, asenkron yönteminiz tamamlanıncaya kadar hücrenin kaydırılmış olması ve daha da kötüsü, hücrenin tablonun başka bir satırı için yeniden kullanılmasıdır. Satırın hala görünür olup olmadığını kontrol ederek, görüntüyü yanlışlıkla ekranda kaydırılan bir satırın görüntüsüyle güncellemediğinizden emin olursunuz.

  3. Eldeki soru ile biraz ilgisiz, hala modern sözleşmeler ve API'dan yararlanmak için bunu güncellemeye mecbur hissettim, özellikle:

    • Bir arka plan kuyruğuna NSURLSessiongöndermek yerine kullanın -[NSData contentsOfURL:];

    • Bunun dequeueReusableCellWithIdentifier:forIndexPath:yerine kullanın dequeueReusableCellWithIdentifier:(ancak bu tanımlayıcı için hücre prototipi kullandığınızdan veya sınıfı veya NIB'yi kaydettiğinizden emin olun); ve

    • Kakao adlandırma kurallarına uyan bir sınıf adı kullandım (yani büyük harfle başlayın).

Bu düzeltmelerde bile sorunlar var:

  1. Yukarıdaki kod indirilen görüntüleri önbelleğe almıyor. Bu, bir görüntüyü ekran dışına ve tekrar ekrana kaydırırsanız uygulamanın görüntüyü tekrar almaya çalışabileceği anlamına gelir. Belki de sunucu yanıt başlıklarınızın sunduğu oldukça şeffaf önbelleğe almasına izin verecek kadar şanslı olacaksınız NSURLSessionve NSURLCacheeğer değilse, gereksiz sunucu talepleri yapacak ve çok daha yavaş bir UX sunacaksınız.

  2. Ekran dışına kaydırılan hücreler için istekleri iptal etmiyoruz. Bu nedenle, 100. satıra hızlı bir şekilde kaydırırsanız, o satırın görüntüsü, artık görünmeyen önceki 99 satırın isteklerinin arkasına geri alınabilir. Her zaman en iyi UX için görünür hücreler için isteklere öncelik verdiğinizden emin olmak istersiniz.

Bu sorunları ele alan en basit düzeltme, SDWebImage veya AFNetworkingUIImageView ile sağlanan gibi bir kategori kullanmaktır . İsterseniz, yukarıdaki sorunlarla başa çıkmak için kendi kodunuzu yazabilirsiniz, ancak bu çok fazla iştir ve yukarıdaki kategoriler bunu sizin için zaten yapmıştır.UIImageView


1
Teşekkürler. Cevabınızı düzenlemeniz gerektiğine inanıyorum. updateCell.poster.image = niliçin cell.poster.image = nil;o ilan edilmeden önce updateCell denir.
Segev

1
Benim app json çok kullanır bu yüzden AFNetworkingkesinlikle gitmek için bir yoldur. Ben biliyordum ama kullanmak için tembel oldu. Ben sadece önbellek onların basit kod satırı ile nasıl çalıştığını hayranım. [imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
Segev

2
Yukarıdakilerin hepsini denedim ve SDWebImage (aslında burada durdu ve AFNetworking'i denemeye bile gerek yoktu) ve bu çok iyi bir seçim oldu. Teşekkürler @Rob.
mondousage

1
Görüntüyü yüklemeyi bitirip görüntü görünümünü güncellediğinizde, etkinlik göstergesini kaldırın. Tek hile, görüntü alınırken hücre görünmüyorsa ve hücre başka bir satır için yeniden kullanılırsa ne olacağını tahmin etmeniz, mevcut herhangi bir etkinlik göstergesinin varlığını tespit etmeniz ve kaldır / güncellemeniz gerekir. yalnızca hücrenin mevcut bir gösterge içermediğini varsaymakla kalmaz.
Rob

1
Orijinal soru "neden cellForRowAtIndexPathhızlıca kaydırdığımda görüntülerin titremesiyle sonuçlanıyor" idi ve bunun neden olduğunu ve nasıl düzeltileceğini açıkladım. Ama neden bunun bile yetersiz olduğunu açıklamaya devam ettim, birkaç daha derin problemi açıkladım ve bunu daha zarif bir şekilde ele almak için neden bu kütüphanelerden biriyle birlikte daha iyi olacağınızı tartıştım (görünür hücreler için isteklere öncelik verin, yedekli ağdan kaçınmak için önbellekleme) istekleri vb.). "Tablo görünümünde titreyen görüntüleri nasıl durdurabilirim" sorusuna yanıt olarak başka ne beklediğinizden emin değilim.
Rob

15

/ * Bu şekilde yaptım ve test ettim * /

Adım 1 = viewDidLoad yönteminde böyle bir tablo için özel hücre sınıfını (tablodaki prototip hücre olması durumunda) veya ucu (özel hücre için özel uç olması durumunda) kaydedin:

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

VEYA

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

Adım 2 = UITableView'ın "dequeueReusableCellWithIdentifier: forIndexPath:" yöntemini şu şekilde kullanın (bunun için sınıfı veya ucu kaydetmeniz gerekir):

   - (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
Son blok içinde cellForRowAtIndexPath çağrılması, her şeyin ikinci kez tetiklenmesine neden olmaz mı?
Mark Bridges

@MarkBridges, Hayır. Aslında burada tableView'ın cellForRowAtIndexPath yöntemini çağırıyorum. TableView'in veri kaynağı yöntemiyle aynı ada sahip olmayın. Gerekli, [self tableView: tableView cellForRowAtIndexPath: indexPath]; Umarım bu karışıklığınızı giderir.
Nitesh Borad

14

Bu sorunu çözen birden fazla çerçeve var. Sadece birkaç isim:

Swift:

Objective-C:


Dikkat edilmesi gereken başka çerçeveler varsa lütfen önerilerinizi ekleyin.
kean

3
Aslında SDWebImagebu sorunu çözmez. Görüntünün ne zaman indirileceğini kontrol edebilirsiniz, ancak bunu yapma iznini sormadan SDWebImagegörüntüyü atayabilirsiniz UIImageView. Temel olarak, sorunun sorunu hala bu kütüphane ile çözülmemiştir.
Bartłomiej Semańczyk

Sorudaki sorun, yazarın hücrenin tekrar kullanılıp kullanılmadığını kontrol etmemesiydi. Bu, SDWebImage dahil olmak üzere bu çerçevelerle ele alınan çok temel bir sorundur.
kean

SDWebImage, iOS 8'den beri çok laggy, benim fav çerçevelerimden biriydi ama şimdi gerçekten iyi çalışan PinRemoteImage kullanmaya başlıyorum.
Joan Cardona

@ BartłomiejSemańczyk Haklısınız, bu sorun SDWebimage tarafından çözülmüyor
19

9

Hızlı 3

NSCache kullanarak görüntü yükleyici için kendi hafif uygulamamı yazıyorum. Hücre görüntüsü titremiyor!

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

Kullanım örneği

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
sana teşekkür ederim derim. ancak kodun biraz sorunu var. izin veri = denemek? Veriler (contentOf: url) {// lütfen URL'yi yerine değiştirin. birçok insana yardımcı olur.
Carl Hung

2
Kod olduğu gibi, dosyayı ağ üzerinden iki kez indirirsiniz: bir kez downloadTaks'da, bir kez Veri ile (cntentsOf :). İndirme görevi basitçe ağ üzerinden indirip verileri geçici bir dosyaya yazıp size localUrl (sizin durumunuzdaki konum) ilettiği için, URL yerine kullanıcı konumunu kullanmalısınız. Bu nedenle, verilerin yalnızca dosyadan okunabilmesi için yerel URL'yi göstermesi gerekir.
Stéphane de Luca

Kullanım Örneği'nde, "ImageCacheLoader.obtainImageWithPath (imagePath: viewModel.image) ......." olması gerekiyor mu?
Tim Kruger

çok hızlı kaydırırken çalışmaz, görüntüler hücre yeniden kullanımı nedeniyle birçok kez değişecektir.
Juan Boero

5

İşte hızlı sürümü (@Nitesh Borad objektif C kodunu kullanarak): -

   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

En iyi yanıt bunu yapmanın doğru yolu değildir :(. Aslında indexPath'i her zaman iyi olmayan modelle bağladınız. Resim yüklenirken bazı satırların eklendiğini düşünün. Şimdi verilen indexPath hücresi ekranda var, ancak görüntü Durum pek olası değildir ve çoğaltılması zordur, ancak mümkündür.

MVVM yaklaşımını kullanmak, hücreyi denetleyicide viewModel ile bağlamak ve viewModel'e görüntü yüklemek (switchToLatest yöntemiyle ReactiveCocoa sinyali atamak), daha sonra bu sinyali abone edin ve hücreye görüntü atamak daha iyidir! ;)

MVVM'yi kötüye kullanmamayı hatırlamanız gerekir. Görüşlerin basit olması lazım! Oysa ViewModels yeniden kullanılabilir olmalı! Bu nedenle View (UITableViewCell) ve ViewModel'i denetleyicide bağlamak çok önemlidir.


1
Evet, benim dizin yolu "taktik düzeltme" (ki ben tavsiye değildi, ama daha ziyade sadece OP sorununu çözmek için en mütevazı düzenleme idi) bu sorundan muzdarip (ama sadece tablo görünümü satırları eklenmiş / silinmiş devam ederse). Ve eğer bu fenomen kendini gösterdiyse, diğer yolları da düzeltebilirim (aynı dizin yolunu aramak yerine, sadece uygun satır için sorgu modeli). Ancak bu taktiksel düzeltmenin (yukarıda açıkladığım) burada ortaya koyduğunuzdan daha da korkunç sorunları var. UIImageViewTavsiye ettiğim kategori çözümünü kullanırsanız, dizin yolları ile ilgili böyle bir sorun yoktur.
Rob

2
Biraz bilgiç gelebilirim, ama VIEW'dan her türlü mantığı çağırmak bu mimariyi kötüye kullanıyor.
badeleux

3

Benim durumumda, görüntü önbellekleme (Kullanılan SDWebImage) nedeniyle değildi. Bunun nedeni, indexPath.row ile özel hücrenin etiket uyuşmazlığıydı.

CellForRowAtIndexPath üzerinde:

1) Özel hücrenize bir dizin değeri atayın. Örneğin,

cell.tag = indexPath.row

2) Ana iş parçacığında, görüntüyü atamadan önce görüntünün etiketi ile eşleşerek ilgili hücreye ait olup olmadığını kontrol edin.

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

2

Teşekkür ederim "Rob" .... UICollectionView ile aynı sorunu vardı ve cevabın benim sorunumu çözmeme yardımcı olur. İşte benim kod:

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

Benim mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];için asla sıfır değil, bu yüzden hiçbir etkisi yok.
karbokatyon

1
hücrenizin görünür olup olmadığını kontrol edebilirsiniz: for (mycell * updateCell in collectionView.visibleCells) {cellVisible = YES; } if (cellVisible) {cell.coverImageView.imageURL = [NSURL URLWithString: [Dict valueForKey: @ "ImageURL"]]; } Benim için de işe yarıyor
sneha

@sneha Evet, böyle yineleyerek görünür olup olmadığını kontrol edebilirsiniz visibleCells, ancak [collectionView cellForItemAtIndexPath:indexPath]kullanımın daha verimli olduğundan şüpheleniyorum (ve bu nedenle ilk etapta bunu yapıyorsunuz).
Rob

@sneha Bu arada, bu yanıttaki kod örneğinizde, yukarıda updateCellolup olmadığını kontrol edersiniz nil, ancak daha sonra kullanmazsınız. Sadece koleksiyon görünümü hücresinin hala görünür olup olmadığını belirlemek için değil, aynı zamanda updateCellbu bloğun içinde de kullanmalısınız.cell (artık geçerli olmayabilir). Ve açıkçası, eğer öyleyse nil, hiçbir şey yapmanıza gerek yoktur (çünkü bu hücre görünmez).
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

Ben arka planda hücre için görüntü yükleme sırasında hücre yükleme hızlandırmak istiyorum düşünüyorum. Bunun için aşağıdaki adımları gerçekleştirdik:

  1. Dosyayı kontrol etmek belge dizininde olup olmadığını kontrol eder.

  2. Değilse, görüntüyü ilk kez yükleyin ve telefon belgesi dizinimize kaydedin. Görüntüyü telefona kaydetmek istemiyorsanız, hücre görüntülerini doğrudan arka plana yükleyebilirsiniz.

  3. Şimdi yükleme işlemi:

Sadece şunu ekleyin: #import "ManabImageOperations.h"

Bir hücre için kod aşağıdaki gibidir:

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

Herhangi bir sorun olursa lütfen cevabı kontrol edin ve yorum yapın ....


0

Basitçe değiştirin,

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

içine

    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'nizi iletebilirsiniz,

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

sebebini öğrenebilir miyim
Kullanıcı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
Herhangi bir açıklama yapmadan kod dökümleri nadiren yardımcı olur. Lütfen bir bağlam sağlamak için bu cevabı düzenlemeyi düşünün.
Chris
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.