Search code examples
iosiphoneswiftuicollectionviewurlrequest

Cells overlapping in UICollectionView


I am displaying the results of a web request to a custom API in an UICollectionView with dynamically sized cells. However, when I call reloadSections after having fetched the data, many of the cells display overlapping.

overlap cells

As the view is scrolled, the cells display in their proper place.

I have tracked down the error to calling reloadSections in the completion block for my web request. Using hardcoded text, the collection view displays properly with the initial layout. If reloadSections is called in the completion block, the error occurs. Below is the relevant code:

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.register(UINib.init(nibName: "ExhibitionsCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "ExhibitionsCollectionViewCell")
    layout.estimatedItemSize = CGSize(width: 1, height: 1)

    collectionView.dataSource = exhibitionsDataSource
    collectionView.delegate = self

    store.fetchExhibitions {
        (exhibitionsResult) -> Void in

        switch exhibitionsResult {
        case let .success(exhibitions):
            print("Successfully found \(exhibitions.count) exhibitions.")
            if exhibitions.first != nil {
                self.exhibitionsDataSource.exhibitions = exhibitions
            }
        case let .failure(error):
            print("Error fetching exhibitions: \(error)")
            self.exhibitionsDataSource.exhibitions.removeAll()
        }
        // this is the line that produces the erroneous layout
        self.collectionView.reloadSections(IndexSet(integer: 0))
    }

}

Code making the web request:

func fetchExhibitions(completion: @escaping (ExhibitionsResult) -> Void) {

    let url = WebAPI.exhibitionsURL
    let request = URLRequest(url: url)
    let task = session.dataTask(with: request) {
        (data, response, error) -> Void in

        let result = self.processExhibitionsRequest(data: data, error: error)
        OperationQueue.main.addOperation {
            completion(result)
        }

    }

    task.resume()
}

If there is any other code that would be helpful in answering this question, let me know.

Update: I fixed the issue by implementing the following delegate method of UICollectionViewDelegateFlowLayout

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let cell = collectionView.cellForItem(at: indexPath)
    let height = cell?.contentView.bounds.height ?? 1
    let width = cell?.contentView.bounds.width ?? 1


    return CGSize(width: width, height: height)
}

Update2: Upon switching out my fake Lorem Ipsum data for the data actually retrieved from the server the problem resurfaced. However, I was able to finally solve the issue simply by switching the line

self.collectionView.reloadSections(IndexSet(integer: 0))

to

self.reloadData()

I would think that reloadSection(_:) simply performs the same action as reloadData() but for the selected sections and the documentation doesn't seem to indicate otherwise...but either way it works now!


Solution

  • From that code I'm not sure of the reason, but maybe try to implement some of the functions provided by the delegate UICollectionViewDelegateFlowLayout.

    Focus on the function

    sizeForItemAtIndexPath

    Example:

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
            return CGSizeMake(320, 500);
        }