How to calculate the cell size so that different devices display the same number of cells in row and column, and the cell height is adjusted to the screen size of the device?
I tried calculating it this way, but it's not right.

Where did I make a mistake?
override func viewDidLoad() {
super.viewDidLoad()
let homeFlowLayout = UICollectionViewFlowLayout()
let itemsPerRow: CGFloat = 2
let itemsPerColumn: CGFloat = 2
let window = UIApplication.shared.connectedScenes.first as? UIWindowScene
let tabBarHeight = window?.windows.first?.safeAreaInsets.bottom ?? 0
let availableWidth = UIScreen.main.bounds.width
let width = availableWidth / itemsPerRow
let availableHeight = UIScreen.main.bounds.height - tabBarHeight
let height = availableHeight / itemsPerColumn
homeFlowLayout.itemSize = CGSize(width: width, height: height)
homeFlowLayout.minimumLineSpacing = 10
homeFlowLayout.scrollDirection = .vertical
collectionView.setCollectionViewLayout(homeFlowLayout, animated: false)
}
Ideally, rather than calculating the size ourselves, we would rather let the framework do it.
Now, this may be beyond the scope of what you might contemplate, but compositional layouts (as described in the Apple Implementing Modern Collection Views sample and in WWDC 2020’s Advances in Collection View Layout) gracefully handle heights as a percentage of the collection view (as well as orthogonal scrolling which you posted in a prior question).
To get five rows, I just defined the
fractionalHeightof the group to be ⅕ (and set theorthogonalScrollingbehavior):That yields:
The only trick that I employed was regarding the spacing. If I set the height of each group to be ⅕ and added spacing, then the total height was too tall by the sum of the spacing. So, I just left it with no spacing, but then inset the content within my cells (the images in my example) to achieve the desired spacing. Maybe there is another way to achieve exactly ⅕ height including spacing, but it was not jumping out at me.
I freely acknowledge that if you have not played around with compositional layouts before, this might be a lot to take in. But when you throw in the orthogonal scrolling (if, indeed, that is still desired), then I think compositional layout really starts to shine. I’ve done the old “collection view inside a table view cell” technique, but it gets mightily ugly very quickly. The compositional layout might feel alien at first, but it is worth it, IMHO.
The aforementioned sample code has a rich array of examples, so I would encourage you to check that out.
All that having been said, if you wanted to calculate the cell sizes yourself using traditional techniques, there are a variety of approaches one could adopt.
But we would avoid using
UIScreen.main. It might feel convenient at this point, but will break if we later support iPads (with their split-screen multitasking), device rotations, etc. We should perform calculations on the basis of theboundsof the content view, not the device’s main screen. That also largely eliminates brittle code that subtracts out the height of navigation bars, tab bars, etc. Just use theboundsof the collection view. Perhaps:The next question is where you call this. The problem is that
viewDidLoadis too early in the view rendering process, so theboundsof the collection view may not yet be reliable. There are lots of approaches, but you could do it inviewDidLayoutSubviews:There are other approaches, but the key observation is that (a) you should just use the
boundsof the collection view; but that (b)viewDidLoadis too early in the process.