Peter Pik Peter Pik - 1 month ago 14
iOS Question

Different cell sizes with an collectionView based on constraints

I've created a collectionView where i have 3 cells the first cells has to fill first row and the third should fill second row however i can't seem to figure out how to do this, since everytime i try to do in

sizeForItemAt
i get that the collectionView frame is
(0, 0, 0, 0)
.

Here is what i have at the moment

class FlowLayout: UICollectionViewFlowLayout {

var numberOfCells: Int!

override init() {
super.init()
setupLayout()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupLayout()
}

func setupLayout() {
minimumInteritemSpacing = 1
minimumLineSpacing = 1

}

override var itemSize: CGSize {
set {

}
get {
let numberOfColumns: CGFloat = 2

let itemWidth = (self.collectionView!.frame.width - (numberOfColumns - 1)) / numberOfColumns
let itemHeight = self.collectionView!.frame.height / 2


return CGSize(width: itemWidth, height: itemHeight)
}
}


}


illustration of what i want

setting up collectionView in my view

func createCollectionView() {
let layout = FlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.numberOfCells = 2



collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.white
collectionView.isScrollEnabled = false
self.topWrapperView.addSubview(collectionView)


collectionView.snp.makeConstraints { (make) -> Void in

make.left.equalTo(topWrapperView).offset(0)
make.top.equalTo(topWrapperView).offset(0)
make.bottom.equalTo(topWrapperView).offset(0)
make.right.equalTo(topWrapperView).offset(0)
}
}


enter image description here

Answer

I had a similar situation except it was the first cell that I wanted 2 cells in first row, with 3 each row after. Used it for a main profile pic and bio in a collection view of images. I successfully implemented it using sizeForItemAtIndexPath with default UICollectionViewDelegateFlowLayout. Here is something that should work for your situation:

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

let insets = collectionView.contentInset
let collectionViewWidth = collectionView.frame.width - (insets.left + insets.right + 1)
let collectionViewHeight = collectionView.frame.height - (insets.top + insets.bottom)

  switch indexPath.row {
   case 0, 1:

   return CGSize(width: collectionViewWidth / 2, height: collectionViewHeight / 2)

    default:
    return CGSize(width: collectionViewWidth, height: collectionViewHeight / 2)
  }
}

You can change what you divide height by to get desired height.

This is what I wrote for my situation, figured I'd post it incase it can help you or anyone else who reads it. If you want it to change with rotation, use viewWillTransition and set a bool flag with a didSet listener:

 var isPortraitOrientation: Bool = true {
   didSet {
 if oldValue != isPortraitOrientation {
    collectionView?.collectionViewLayout.invalidateLayout()
     }
   }
} 

 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
   super.viewWillTransition(to: size, with: coordinator)
  isPortraitOrientation = (size.width - size.height) <= 0 ? true : false
}


 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let insets = collectionView.contentInset
    let collectionViewWidth = collectionView.frame.width - (insets.left + insets.right + 1)
    let collectionViewHeight = collectionView.frame.height - (insets.top + insets.bottom)

    switch indexPath.row {
    case 0:

        if textViewSelected {
            return isPortraitOrientation ? CGSize(width: collectionViewWidth, height: collectionViewHeight / 2) : CGSize(width: collectionViewWidth, height: collectionViewWidth / 3)
        } else {
            return isPortraitOrientation ? CGSize(width: collectionViewWidth / 2, height: collectionViewWidth / 2) : CGSize(width: collectionViewWidth / 2, height: collectionViewHeight / 1.5)
        }
    case 1:
        return isPortraitOrientation ? CGSize(width: collectionViewWidth / 2, height: collectionViewWidth / 2) : CGSize(width: collectionViewWidth / 2, height: collectionViewHeight / 1.5)

    default:
        return isPortraitOrientation ? CGSize(width: collectionViewWidth / 3, height: collectionViewWidth / 3) : CGSize(width: collectionViewWidth / 5, height: collectionViewWidth / 5)
    }
}

EDIT:

based on your additional code, instead of doing CGRect.zero for the collectionView frame use topWrapperView.bounds.