Tristan Warner-Smith
Tristan Warner-Smith

Reputation: 9771

How to configure a UICollectionView Cell Size per Size Class?

I'm finding friction when trying to create responsive / adaptive UICollectionViewCells in the UIStoryboard.

The issue I'm seeing is that you don't seem to be able to set the Cell Size per Size Class and I'm trying to ascertain the right approach to this. I've designed the cells to adjust to their containers, so that they should autosize regardless of size class. This mostly works in that if I change the size class, select my cell view and do Update Frames then they all resize to fit their new size. However it's a one shot deal, if I go back to the Any/Any size class then I'm still seeing that resized version.

Here's what I'm aware I could try:

I'm hoping this is a solved scenario and I'm just missing something, but I'm aware that it could be that the IB tools don't match the code at this point.

Upvotes: 20

Views: 10105

Answers (4)

Ariven Nadar
Ariven Nadar

Reputation: 1318

Swift 4
Hi Fellow Developers,

This is easy to do if the height and width of the UICollectionViewCell are same.

enter image description here

Steps
1. Import ** UICollectionViewDelegateFlowLayout**
2. Add the sizeForItem IndexPath method of UICOllectionView as follows

func collectionView(_ collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAt indexPath:IndexPath) -> CGSize {
    return CGSize(width: collectionVw.frame.size.height, height: collectionVw.frame.size.height)
}   

Note: What happening is you are setting the height of the UICollectionView as height and width of the UICollectionViewCell

Happy Coding :)

Upvotes: 0

Alvin George
Alvin George

Reputation: 14296

I was stuck with same problem after implementing size class(iPad and iPhone).Well, I figured out a solution. Hope it helps!

Implement UICollectionViewDelegateFlowLayout.

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
    {
         var device = UIDevice.currentDevice().model  
         var cellSize:CGSize = CGSizeMake(155, 109)

         if (device == "iPad" || device == "iPad Simulator") {
            cellSize = CGSizeMake(240, 220) 
         }

         return cellSize
    }

Upvotes: 1

Martin Woolstenhulme
Martin Woolstenhulme

Reputation: 4038

Here's a similar solution coded-out in Swift. I just styled both of my cells in the storyboard and leave them viewable for any size class combination. When the trait collection changes I update the cellSize and cellReuseID I want to use and tell the collectionView to reload all the visible cells. Then

collectionView(collectionView: UICollectionView,
    layout collectionViewLayout: UICollectionViewLayout,
    sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize

and

override func collectionView(collectionView: UICollectionView,
    cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell

(not shown in sample code) are called which lets me update the size of the cell and update the cell's storyboard styling. So not entirely done in storyboard, but good enough until more support is provided in Xcode.

struct MyCollectionViewConstants{
    static let CELL_ANY_ANY_REUSE_ID = "cell";
    static let CELL_COMPACT_REGULAR_REUSE_ID = "cellSmall"
    static let CELL_ANY_ANY_SIZE = 100;
    static let CELL_COMPACT_REGULAR_SIZE = 70;
}

class MyCollectionView: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    var cellSize = MyCollectionViewConstants.CELL_ANY_ANY_SIZE
    var cellReuseID = MyCollectionViewConstants.CELL_ANY_ANY_REUSE_ID


    func collectionView(collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{

        return CGSize(width: cellSize, height: cellSize)
    }

    override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)

        if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact
            && self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClass.Regular){
            cellSize = MyCollectionViewConstants.CELL_COMPACT_REGULAR_SIZE
            cellReuseID = MyCollectionViewConstants.CELL_COMPACT_REGULAR_REUSE_ID
        } else {
            cellSize = MyCollectionViewConstants.CELL_ANY_ANY_SIZE
            cellReuseID = MyCollectionViewConstants.CELL_ANY_ANY_REUSE_ID
        }

        self.collectionView.reloadItemsAtIndexPaths(
            self.collectionView.indexPathsForVisibleItems())
    }
}

Upvotes: 7

Tristan Warner-Smith
Tristan Warner-Smith

Reputation: 9771

The solution I came up with was just to implement the UICollectionViewDelegateFlowLayout and implement the sizeForItemAtIndexPath method. This means that the cell dimensions can be set to match the available Size Class dimensions.

This still isn't ideal as you can't see the changes in the storyboard and you can't create a universal design and see it in each of the different formats.

I'm still hoping someone has a better option.

Upvotes: 15

Related Questions