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.
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!
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);
}