ios - UICollectionView cellForItemAtIndexPath 在滚动时一遍又一遍地调用

标签 ios swift

我有一个 UICollectionView,其中每个单元格都是从 Flickr 异步填充的。

在我的场景中,我想要 21 个单元格,我的代码可以很好地生成它。但是,即使在 cellForItemAtIndexPath 方法中填充了所有 21 个单元格之后,我的代码还是一次又一次地调用 Flickr。我查看了 Apple 文档,但没有看到任何地方声明 cellForItemAtIndexPath 应该在滚动时连续调用。

我希望每个单元格只填充一次,然后一旦完成 cellForItemAtIndexPath 就不应再次调用。

这是我的代码:

import UIKit
import MapKit
import CoreData

class ImagesCollectionViewController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{

    @IBOutlet weak var mapView: MKMapView!

    @IBOutlet weak var collectionView: UICollectionView!
    var mapLat : Double!
    var mapLong : Double!
    let flickrApi = Flickr()
    var imageURLSet = [String]()

    override func viewDidLoad() {

        // set the location and zoom of the minimap
        let clLocation = CLLocationCoordinate2D(latitude: mapLat, longitude: mapLong)
        let span = MKCoordinateSpan(latitudeDelta: 2, longitudeDelta: 2)

        mapView.setRegion(MKCoordinateRegion(center: clLocation, span: span), animated: false)
        mapView.scrollEnabled = false

        getImages()
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return imageURLSet.count
    }

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        print("test") // <--- this prints over and over again on scrolling
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ImageCell", forIndexPath: indexPath) as! FlickrImageCellViewController
        let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
        dispatch_async(queue) { () -> Void in
            let url = NSURL(string: self.imageURLSet[indexPath.row])
            let data = NSData(contentsOfURL: url!)
            let img = UIImage(data: data!)

            dispatch_async(dispatch_get_main_queue(), {
                 cell.imageView.image = img
            })


        }

        // This is where the cells are being populated


        return cell
    }

    //Get images from the flickr API and store in array

    func getImages(){

        let parameters : [String : AnyObject] = ["method": Flickr.Consts.GEO_METHOD, "format" : Flickr.Consts.FORMAT, "api_key": Flickr.Consts.API_KEY, "lat" : mapLat, "long" : mapLong, "nojsoncallback" : "1", "per_page" : "21", "extras" : "url_m"]

        flickrApi.performGetRequest(parameters) { (data, error) in


            for record in data as! [AnyObject]{
                if(record["url_m"] != nil){
                    print("record")
                    print(record["url_m"] as! String)
                    print("end record")
                    self.imageURLSet.append(record["url_m"] as! String)

                }
            }
        }
    }

    override func viewDidAppear(animated: Bool) {

        self.collectionView.reloadData()
    }
}

最佳答案

这是因为 UICollectionView 重用了单元格。每次重新调整单元格的用途时,都会调用 cellForItemAtIndexPath:。显然,每次重复使用一个单元格时都进行网络调用是不理想的。

容纳单元重用和网络调用的最佳方法是实现缓存。这可以是像字典一样简单的东西,其中键是您的索引路径,值是您的 Flickr 图像。 [NSIndexPath: UIImage]

当调用 cellForRowAtIndexPath 时:

1) 使用 indexPath 作为键检查图像的缓存。

2) 如果该键有一个 UIImage,请将其设置在您的单元格中。

3) 如果该索引路径没有缓存的 UIImage,则进行网络调用。当它返回时,将结果添加到您的缓存并更新单元格。

关于您的代码的注释:

由于单元格被重用,您的网络调用可能会在您的 Collection View 单元格显示与发起调用的对象不同的对象时返回。您应该进行某种检查,以确保单元格当前显示的 indexPath 是返回图像的正确索引路径。如果不这样做,可能会导致您为给定数据显示错误的图像。

关于ios - UICollectionView cellForItemAtIndexPath 在滚动时一遍又一遍地调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37169087/

相关文章:

ios - 信号量在执行其后的代码块之前不会等待

ios - SpriteKit 关节 : follow the body

ios - 过滤 [NSDictionary] 时,闭包错误无法使用类型为 'filter' 的参数列表调用 '((_) -> _)'

ios - 强制 Nib 加载器使用 init?(带有 :) for custom subclass of NSObject in iOS

从模拟器关闭应用程序时的 iOS 调试位置监控

ios - 在 Swift 中通过 TouchID 生成哈希或字符串

ios - 在 iOS 上通过 OpenCV Process Image 方法更改标签文本

iphone - iOS 应用使用 sudzc 连接 soap web 服务

XCode 7 中的 iOS 本地化

swift - 如何在 Swift 中实现线程安全哈希表(电话簿)数据结构?