UICollectionView cellForItemAtIndexPath在滚动时反复调用

时间:2016-05-11 17:05:35

标签: 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()
    }
}

1 个答案:

答案 0 :(得分:2)

这是因为UICollectionView重用单元格。每次重新调整单元格时,都会调用cellForItemAtIndexPath :.显然,每次重复使用一个小区时进行网络呼叫并不理想。

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

调用cellForRowAtIndexPath时:

1)使用indexPath作为密钥检查缓存中的图像。

2)如果该键有UIImage,请在单元格中设置。

3)如果该索引路径没有缓存的UIImage,请进行网络呼叫。返回时将结果添加到缓存并更新单元格。

关于代码的说明:

由于单元格被重用,因此您的网络调用可能会在您的集合视图单元格显示与启动调用的对象不同的对象时返回。您应该检查单元格当前显示的indexPath对于返回的图像是否正确。如果不这样做,可能会导致您显示给定数据的错误图像。