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
How to Reference Instance Function When Calling Sequencetype.Foreach
Error When Trying to Call Setdata(From: ) in the Cloud Firestore iOS API. How to Fix It
Firebase .Indexon Dynamic Keys
Html5 Video Autoplay on Iphone
iPhone 6 and 6 Plus Responsive Breakpoints
Mobile Safari Position:Fixed Z-Index Glitch When Scrolling
CSS Gradient Not Working on iOS
Div Scrolling Freezes Sometimes If I Use -Webkit-Overflow-Scrolling
Retain Cycle (Strong Reference) Fix for Custom Uitextfield
Swift Formatting String to Have 2 Decimal Numbers If It Is Not a Whole Number
Swift - How to Save Memory Using Alamofireimage/Imageshack
How to Load a Url Link That Is Inside a Web View and Keep It in That Web View in Swift