如何让XIB中UICollectionView reloadData表现与Storyboard一致
Hey there! I see the issue you're facing: your XIB-based UICollectionView is calling cellForItemAt 100 times right after reloadData, while the Storyboard version only loads the 5 visible cells and loads more as you scroll. Let's break down why this is happening and fix it.
Root Cause
The main problem is in your cell reuse logic. You're both registering the nib via register(UINib...) AND manually loading the nib in cellForItemAt when you assume cell is nil. This bypasses the UICollectionView's reuse pool and cell visibility calculation, forcing it to create all cells upfront instead of only the visible ones.
Step-by-Step Fix
1. Simplify Cell Reuse Code
Since you've already registered the nib in viewDidLoad, the dequeueReusableCell(withReuseIdentifier:for:) method will always return a valid cell (it automatically instantiates from the nib if no reusable cell is available). You can remove the manual nib loading block entirely.
Here's the cleaned-up cellForItemAt method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard collectionView == clvProducts else { return UICollectionViewCell() // Fallback for other collection views } let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductListCell", for: indexPath) as! ProductListCell // Configure cell data let product = self.objCategorywiseResponse.SimilirProductsList![indexPath.row] if let urlString = product.productPic, let url = URL(string: urlString) { cell.imgProduct.kf.setImage(with: url) } if let rating = product.AvgRatingCount { cell.subViewRating.rating = rating } cell.lblShopName.text = product.ShopName cell.lblDistance.setTitle(product.Distance.map { "\($0) km near by you" } ?? "", for: .normal) cell.lblMobileNo.text = product.LandLineNumber // Set up button targets cell.btnAddToCompare.tag = indexPath.row cell.btnAddToCompare.addTarget(self, action: #selector(btnClickedAddToCompare(_:)), for: .touchUpInside) cell.btnAddProduct.tag = indexPath.row cell.btnAddProduct.addTarget(self, action: #selector(btnClickedAddProduct(_:)), for: .touchUpInside) cell.btnCall.tag = indexPath.row cell.btnCall.addTarget(self, action: #selector(callCellactn(_:)), for: .touchUpInside) cell.btnMap.tag = indexPath.row cell.btnMap.addTarget(self, action: #selector(locationCellactn(_:)), for: .touchUpInside) return cell }
2. Verify Collection View Layout Settings
Make sure your UICollectionView has a valid layout (like UICollectionViewFlowLayout) with correct sizing:
- Set a fixed
itemSize(or useestimatedItemSizewith properly configured auto layout constraints in the cell) - Ensure the collection view's frame is constrained properly (not set to a huge height that would fit all 100 cells at once)
- Check that
sectionInsetandminimumLineSpacingare set reasonably
If you're using auto layout for the cell, confirm all constraints are set so the layout can calculate cell sizes correctly without forcing the collection view to preload all cells.
3. Optional: Check Prefetching
By default, UICollectionView has isPrefetchingEnabled = true, which loads a few extra cells ahead of scrolling (not all 100). If you want to disable this entirely, you can set:
clvProducts.isPrefetchingEnabled = false
But this is usually unnecessary—fixing the reuse logic should resolve the main issue.
Why This Works
When you properly use the registered nib and reuse pool, UICollectionView can accurately calculate which cells are visible within its bounds. It only calls cellForItemAt for those visible cells, and reuses cells as you scroll, just like the Storyboard version does.
内容的提问来源于stack exchange,提问作者Abhiram

