Collection View Cell初始可见时子视图Frame无法正常获取的问题
Hey there, let's break down why you're seeing that (0,0,0,0) frame for initial cells and how to fix it quickly.
First, Correct a Critical Mistake in Your Code
Looking at your snippet, you're using self.collectionView(self.cellCollectionView, cellForItemAt: indexPath) to get the selected cell. That's actually calling your data source method, which creates a new instance of the cell (or pulls one from the reuse pool that hasn't been laid out yet) — not the actual cell visible on screen that was just selected. That's a major reason you're getting zero frames initially!
You should instead use collectionView.cellForItem(at: indexPath) to retrieve the already-displayed cell.
Why Initial Cells Return Zero Frames
When the collection view first loads visible cells, the didSelectItemAt delegate method can fire before the cell's subviews finish their layout process. The collection view adds the cell to the view hierarchy first, then triggers layout updates (like layoutSubviews) asynchronously. So when you try to access the subview's frame right away, it hasn't been calculated yet. When you scroll the cell off and back on, the cell has already gone through layout, so the frame is valid.
Solution 1: Force Layout Before Accessing the Frame
The simplest fix is to call layoutIfNeeded() on the cell before getting the subview's frame. This forces the cell (and its subviews) to complete any pending layout updates immediately.
Here's the corrected code:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let window = UIApplication.shared.keyWindow, // Use cellForItem(at:) to get the visible cell let selectedCell = collectionView.cellForItem(at: indexPath) as? postCell else { return } // Force the cell to finish all pending layout updates selectedCell.layoutIfNeeded() let photoItem = selectedCell.photo let photoItemPosition = photoItem.frame let convertedFrame = selectedCell.convert(photoItemPosition, to: window) print(photoItemPosition) print(convertedFrame) }
Solution 2: Cache the Frame in the Cell (More Reliable for Complex Layouts)
If your cell uses Auto Layout or has dynamic content, you can cache the subview's frame directly in the cell once layout is complete. This ensures you always get a valid frame when you need it.
Update your postCell class:
class postCell: UICollectionViewCell { let photo = UIImageView() // A private-set property to store the valid photo frame private(set) var validPhotoFrame: CGRect = .zero override func layoutSubviews() { super.layoutSubviews() // Update the cached frame every time layout completes validPhotoFrame = photo.frame } }
Then in your delegate method:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let window = UIApplication.shared.keyWindow, let selectedCell = collectionView.cellForItem(at: indexPath) as? postCell else { return } selectedCell.layoutIfNeeded() // Use the cached valid frame instead of accessing the subview directly let photoItemPosition = selectedCell.validPhotoFrame let convertedFrame = selectedCell.convert(photoItemPosition, to: window) print(photoItemPosition) print(convertedFrame) }
Solution 3: Wait for the Main Runloop to Complete Layout
If the above fixes don't work (unlikely, but possible for very complex layouts), you can dispatch your frame calculation to the end of the main runloop, which gives the system time to finish all layout tasks:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let window = UIApplication.shared.keyWindow, let selectedCell = collectionView.cellForItem(at: indexPath) as? postCell else { return } DispatchQueue.main.async { let photoItemPosition = selectedCell.photo.frame let convertedFrame = selectedCell.convert(photoItemPosition, to: window) print(photoItemPosition) print(convertedFrame) } }
The first solution should cover most cases, but the second one is great for ensuring consistency across different layout scenarios.
内容的提问来源于stack exchange,提问作者Stefan

