Solution for Ch 21 Silver Challenge: Updated Item Sizes


#1

Select the Collection View in Photorama Scene in Main.storyBoard canvas, at Size Inspector reset all values in “Min Spacing” and “Section Insets” in Collection View section to zero.

Add the code below at the end of PhotosViewController.swift

extension PhotosViewController: UICollectionViewDelegateFlowLayout {
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let collectionViewWidth = collectionView.bounds.size.width
    let numberOfItemsPerRow: CGFloat = 4
    let itemWidth = collectionViewWidth / numberOfItemsPerRow
    
    return CGSize(width: itemWidth, height: itemWidth)
  }
  
  override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    collectionView.reloadData()
  }
}

#2

Thanks, I needed the collectionView.reloadData() to make it work when the device rotates.

There is a problem though : you need to enter 5 in order to have 4 items per row. I don’t know why is that.


#3

Have you reset all values in “Min Spacing” and “Section Insets” in Collection View section to zero at Size Inspector?

I suggest you to upload two screen captures of your running app in portrait and landscape mode here.


#4

Thanks for sharing this solution!

One thing that confused me is using .reloadData() method. I think it’s unnecessary overhead.
It’s better to use layout’s invalidateLayout() method.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    
    collectionView.collectionViewLayout.invalidateLayout()
}

#5
    if let collectionViewFlowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
        spacingSum = collectionViewFlowLayout.minimumInteritemSpacing * (numberOfItemsPerRow - 1)
        spacingSum += collectionViewFlowLayout.sectionInset.left + collectionViewFlowLayout.sectionInset.right
    }
    let itemWidth = (collectionViewWidth - spacingSum) / numberOfItemsPerRow

#6

Thanks for sharing that solution. Before doing this challenge I checked other tutorials and here is the solution, which works without any reloading. Place this line in PhotoViewController:
let sectionInsets = UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0)
Those are the data from the size inspector we filled out manually. Then create the extension:

extension PhotosViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let itemsPerRow: CGFloat = 4
        
// We calculate paddingSpace. This is possible, because space is the same. 
// We select one of the spaces, right or left

        let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
        let availableWidth = view.frame.width - paddingSpace
        let widthPerItem = availableWidth / itemsPerRow
        return CGSize(width: widthPerItem, height: widthPerItem)
    }

   // This will reset the sectioninsets.

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }
}

#7

One thing is missing - the spacing. We’ve got 2 points between the items and also as section insets. Between 4 items there are 3 spacings + 2 edge insets summing 5 in total and 5 multiplied by 2 is 10 points for all the spacings:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let side = (view.bounds.width - 10.0) / 4.0
    return CGSize(width: side, height: side)
}

You can avoid hardcoding of the spacing by a nice formula:

((number of the items + 1) * (desired spacing value)) / (number of the items)


#8

There’s a bug in your solution:

let availableWidth = view.frame.width - paddingSpace

It must be:

let availableWidth = view.bounds.width - paddingSpace

frame is the outer dimension of a view, while bounds is the inner one for the sub-views.