Why on Xcode 11, Uicollectionviewcell Changes Size as Soon as You Scroll (I Already Set Size in Sizeforitem Atindexpath:)

Auto-Layout for CollectionViewCell: Trailing/Leading constraints change cell size

Cells can now self-size with auto layout and ignore what you return in collectionView(_:layout:sizeForItemAt:). If you want to use collectionView(_:layout:sizeForItemAt:), change Estimate size to None in the storyboard.
https://stackoverflow.com/a/58369142/14351818

The size of UICollectionViewCell will be different when I scroll the screen

Try to use the UICollectionViewDelegateFlowLayout delegate by changing your code like this:

class imageCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

private let reuseIdentifier = "photoCell"
var photos:albumCellModel?

override func viewDidLoad() {
super.viewDidLoad()

navigationController?.toolbarHidden = true
}

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}

override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let photos = photos else { return 0}

return photos.photoSet.count
}

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("photoCell", forIndexPath: indexPath) as! imageCollectionViewCell
cell.imageView.image = photos?.photoSet[indexPath.row].image
return cell
}

// New delegate method
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberOfItemsPerRow: CGFloat = 3
let spacingBetweenCells: CGFloat = 0

let totalSpacing = (2 * spacingBetweenCells) + ((numberOfItemsPerRow - 1) * spacingBetweenCells)

let width = (collectionView.bounds.width - totalSpacing) / numberOfItemsPerRow
let height: CGFloat = 200 // Put the value you desire

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

UICollectionView layout behave weirdly on scrolling

I think your problem could be the same as the problem in this post.

Try fixing your problem by setting the UICollectionView's estimatedSize to none.

This is because in Xcode 11 Cells in a CollectionView can now self-size with Auto Layout constrained views in the canvas.

UICollectionView Self Sizing Cells with Auto Layout

This answer is outdated from iOS 14 with the addition of compositional layouts. Please consider updating the new API

Updated for Swift 5

preferredLayoutAttributesFittingAttributes renamed to preferredLayoutAttributesFitting and use auto sizing



Updated for Swift 4

systemLayoutSizeFittingSize renamed to systemLayoutSizeFitting



Updated for iOS 9

After seeing my GitHub solution break under iOS 9 I finally got the time to investigate the issue fully. I have now updated the repo to include several examples of different configurations for self sizing cells. My conclusion is that self sizing cells are great in theory but messy in practice. A word of caution when proceeding with self sizing cells.

TL;DR

Check out my GitHub project


Self sizing cells are only supported with flow layout so make sure thats what you are using.

There are two things you need to setup for self sizing cells to work.

#1. Set estimatedItemSize on UICollectionViewFlowLayout

Flow layout will become dynamic in nature once you set the estimatedItemSize property.

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

#2. Add support for sizing on your cell subclass

This comes in 2 flavours; Auto-Layout or custom override of preferredLayoutAttributesFittingAttributes.

Create and configure cells with Auto Layout

I won't go to in to detail about this as there's a brilliant SO post about configuring constraints for a cell. Just be wary that Xcode 6 broke a bunch of stuff with iOS 7 so, if you support iOS 7, you will need to do stuff like ensure the autoresizingMask is set on the cell's contentView and that the contentView's bounds is set as the cell's bounds when the cell is loaded (i.e. awakeFromNib).

Things you do need to be aware of is that your cell needs to be more seriously constrained than a Table View Cell. For instance, if you want your width to be dynamic then your cell needs a height constraint. Likewise, if you want the height to be dynamic then you will need a width constraint to your cell.

Implement preferredLayoutAttributesFittingAttributes in your custom cell

When this function is called your view has already been configured with content (i.e. cellForItem has been called). Assuming your constraints have been appropriately set you could have an implementation like this:

//forces the system to do one layout pass
var isHeightCalculated: Bool = false

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//Exhibit A - We need to cache our calculation to prevent a crash.
if !isHeightCalculated {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var newFrame = layoutAttributes.frame
newFrame.size.width = CGFloat(ceilf(Float(size.width)))
layoutAttributes.frame = newFrame
isHeightCalculated = true
}
return layoutAttributes
}

NOTE On iOS 9 the behaviour changed a bit that could cause crashes on your implementation if you are not careful (See more here). When you implement preferredLayoutAttributesFittingAttributes you need to ensure that you only change the frame of your layout attributes once. If you don't do this the layout will call your implementation indefinitely and eventually crash. One solution is to cache the calculated size in your cell and invalidate this anytime you reuse the cell or change its content as I have done with the isHeightCalculated property.

Experience your layout

At this point you should have 'functioning' dynamic cells in your collectionView. I haven't yet found the out-of-the box solution sufficient during my tests so feel free to comment if you have. It still feels like UITableView wins the battle for dynamic sizing IMHO.

##Caveats
Be very mindful that if you are using prototype cells to calculate the estimatedItemSize - this will break if your XIB uses size classes. The reason for this is that when you load your cell from a XIB its size class will be configured with Undefined. This will only be broken on iOS 8 and up since on iOS 7 the size class will be loaded based on the device (iPad = Regular-Any, iPhone = Compact-Any). You can either set the estimatedItemSize without loading the XIB, or you can load the cell from the XIB, add it to the collectionView (this will set the traitCollection), perform the layout, and then remove it from the superview. Alternatively you could also make your cell override the traitCollection getter and return the appropriate traits. It's up to you.

Let me know if I missed anything, hope I helped and good luck coding



sizeForItemAt not called while scrolling

I just got it's solution this problem arises in Xcode 11+, and solution to it is set collection view automatic size to "None".
Refer: Why on Xcode 11, UICollectionViewCell changes size as soon as you scroll (I already set size in sizeForItem AtIndexPath:)?

How to have multiple columns in tableview or alternative view?

They will most likely have used a UITableView with custom UITableView cells.

Using an XIB (nib) view to create the UI for a row and then passing the data in.

Check out here and here for more info.



Related Topics



Leave a reply



Submit