By rawbee


2014-09-17 16:02:15 8 Comments

I'm trying to get self sizing UICollectionViewCells working with Auto Layout, but I can't seem to get the cells to size themselves to the content. I'm having trouble understanding how the cell's size is updated from the contents of what's inside the cell's contentView.

Here's the setup I've tried:

  • Custom UICollectionViewCell with a UITextView in its contentView.
  • Scrolling for the UITextView is disabled.
  • The contentView's horizontal constraint is: "H:|[_textView(320)]", i.e. the UITextView is pinned to the left of the cell with an explicit width of 320.
  • The contentView's vertical constraint is: "V:|-0-[_textView]", i.e. the UITextView pinned to the top of the cell.
  • The UITextView has a height constraint set to a constant which the UITextView reports will fit the text.

Here's what it looks like with the cell background set to red, and the UITextView background set to Blue: cell background red, UITextView background blue

I put the project that I've been playing with on GitHub here.

13 comments

@Marmoy 2017-07-16 10:45:36

In iOS 10+ this is a very simple 2 step process.

  1. Ensure that all your cell contents are placed within a single UIView (or inside a descendant of UIView like UIStackView which simplifies autolayout a lot). Just like with dynamically resizing UITableViewCells, the whole view hierarchy needs to have constraints configured, from the outermost container to the innermost view. That includes constraints between the UICollectionViewCell and the immediate childview

  2. Instruct the flowlayout of your UICollectionView to size automatically

    yourFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    

@James Heald 2017-12-01 02:29:23

Im curious, how would wrapping your cells contents in a UIView make Auto Layout perform better? Id have thought it would perform better with a more shallow hierarchy?

@Marmoy 2017-12-01 08:40:52

I did not mention performance, only simplicity. Self-sizing cells will only work if you have constraints spanning all the way between left and right, and between top and bottom. Achieving that is easiest when all views are wrapped in a single container. If that container is a UIStackView it is easier that if it is any other descendant of UIView.

@ZhengGe Che 2019-01-25 14:58:51

Can you please tell me how to set height constant (like set height 25, and width will be automatically changed) for UICollectionViewFlowLayoutAutomaticSize.

@LinusGeffarth 2019-02-22 17:03:18

Using Swift 4.1, Xcode tells me UICollectionViewFlowLayout.automaticSize has been renamed to UICollectionViewFlowLayoutAutomaticSize.

@pawel221 2017-03-22 10:26:43

In iOS10 there is new constant called UICollectionViewFlowLayout.automaticSize (formerly UICollectionViewFlowLayoutAutomaticSize), so instead:

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

you can use this:

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

It has better performance especially when cells in you collection view has constant wid

@Mobile Bloke 2017-04-01 10:31:16

Where do you set this? in viewdidload? How do you get a handle on flowlayout?

@Mobile Bloke 2017-04-07 07:38:22

I managed to implement this - but it has a strange behaviour that makes it center the cells if there is a single cell. Here is the stack overflow question about it - has me stumped: stackoverflow.com/questions/43266924/…

@d4Rk 2018-03-19 12:08:01

You can access the flow layout via the collectionViews collectionViewLayout and just check that it is of type UICollectionViewFlowLayout

@user924 2019-04-14 16:35:11

it makes my cells very small, useless

@Matt Koala 2015-07-07 21:35:25

A few key changes to Daniel Galasko's answer fixed all my problems. Unfortunately, I don't have enough reputation to comment directly (yet).

In step 1, when using Auto Layout, simply add a single parent UIView to the cell. EVERYTHING inside the cell must be a subview of the parent. That answered all of my problems. While Xcode adds this for UITableViewCells automatically, it doesn't (but it should) for UICollectionViewCells. According to the docs:

To configure the appearance of your cell, add the views needed to present the data item’s content as subviews to the view in the contentView property. Do not directly add subviews to the cell itself.

Then skip step 3 entirely. It isn't needed.

@Daniel Galasko 2015-09-02 05:13:19

Interesting; this is specifically for Xibs but nevertheless thanks, will try and muddle with the Github project and see if I can reproduce. Maybe divide the answer into Xib vs programmatic

@ProblemSlover 2015-09-07 17:10:45

Very Helpful . Thanks!

@Nicolas Miari 2015-10-06 11:06:10

iOS 9, Xcode 7. Cells are protoyped in storyboard, set custom subclass. Tried creating a property called contentView, Xcode complains it conflicts with existing property. Tried adding subviews to self.contentView and set constraints to that, then app crashes.

@Matt Koala 2015-10-07 18:56:03

@NicolasMiari I don't know about how to do this programmatically, my solution is really just to add a single uiview in the XIB where everything goes inside it. You don't need to create a property or anything.

@wasabi 2015-12-06 17:22:40

I'm facing the same problem as @NicolasMiari. When I set the estimatedContentSize for cells prototyped in storyboard with autolayout constraints on iOS 9 / Xcode 7 the app crashes with a bad access exception and no helpful stacktrace

@Matt Koala 2015-12-07 06:10:52

I'm not sure what the confusion is. I believe that @NicolasMiari wasn't following my instructions properly. You don't need a property "contentView". You don't need to "add subviews to self.contentView". In your prototyped cells, make sure everything is inside a single UIView. That's it.

@Nicolas Miari 2015-12-07 06:27:31

When you drag a view into the cell in IB, top level, you are effectively adding a subview to the cell's contentView, right?

@Matt Koala 2015-12-07 07:11:48

No. You aren't adding a subview to the contentView. I believe you are actually creating the contentView itself with the view I'm saying to add.

@Daniel Galasko 2016-06-04 10:05:22

Would love a link to a sample project to see this in action. Storyboard and programmatic would be great:)

@Matt Koala 2016-06-09 19:50:22

@Philippe Sabourin 2017-01-25 18:44:32

Based on what i'm seeing here adding the views directly to the cell in storyboard are adding it to the content view. I printed the contents of cell.contentView, cell.contentView.subviews, and cell.subviews and without having added a "parent view" to the cell, cell.subviews just has the contentView, and the contentView contains all of the subviews i added in storyboard.

@Honey 2017-12-04 16:53:55

@ProblemSlover I'm confused. Doesn't the cell itself have a contentView? Should we be adding it to that? Why do we need an extra view?

@Daniel Galasko 2014-09-17 17:04:24

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 = CGSize(width: 100, height: 100)

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 preferredLayoutAttributesFittingAttributes(_ 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


@anna 2014-10-06 21:51:01

I'm trying to implement the same thing with flow layout, however when I return it newFrame with updated size, the layout doesn't seem to recalculate y positions, which are still based on the height in estimatedSize. What's missing?

@Daniel Galasko 2014-10-07 04:31:30

@anna are you calling invalidateLayout on the layout?

@anna 2014-10-07 19:34:10

At which point should I be calling invalidate layout?

@Daniel Galasko 2014-10-07 19:39:36

@anna my apologies I thought you were changing your layout and the view was not updating. Is your - (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionVi‌​ewLayoutAttributes *)layoutAttributes being called?

@anna 2014-10-07 19:46:35

yes, it's being called and retuning the correct height for each cell but the contentSize of the flow layout never adjusts to accommodate the new heights.

@Daniel Galasko 2014-10-07 20:06:14

Where are you setting the estimated size property on the collection view layout?

@anna 2014-10-07 20:08:45

Yep, setting it on the layout. I've seen other comments in Apple forums where people had that problem. But not sure if it's a bug or you have a way to do it if it's working for you?

@Daniel Galasko 2014-10-07 20:16:05

I'm assuming you started with a brand new project with a single collection view and you testing with that? I can send you a sample project in the morning?:)

@anna 2014-10-07 20:22:34

Correct, brand new project. Sure, if you want to put up a sample on GitHub or something, would be great!

@Daniel Galasko 2014-10-08 07:50:59

@anna 2014-10-08 13:29:23

Thanks Daniel. One difference is that you're using Storyboard and I wasn't, wonder what extra setting does that add. But even so, in your project there's still weird behavior such that all the cells don't show up until scrolling begins and the last cell is off. Makes me suspect the flow layout is not well implemented for self-sizing :S

@Daniel Galasko 2014-10-08 13:32:29

@anna I also noticed the odd disappearing cells but i think thats a bug with UITextView because when i debugged the views there was a strange image embedded inside the text view. You should be able to use it without a storyboard

@anna 2014-10-08 14:15:15

Ah, found the difference, you set the estimated height pretty high (400) whereas I was doing the minimum (44). That helped. But the contentSize still seems to not be getting set correctly until you scroll all that way through, so still not very usable. Btw, I changed UITextView to UILabel, same behaviour.

@Daniel Galasko 2014-10-08 14:32:30

@anna what a pity that it is running so averagely! At least you somewhere though?:)

@anna 2014-10-08 15:18:23

Yeah, thanks for helping to solve part of the mystery! But I don't think the flow layout is ready enough to use in production. Probably will stick with the old methods a while longer..

@LenK 2014-12-04 01:09:31

I have also tried for many hours to get dynamic sized cells in a flow layout to work and they work about 90% of the time. But some of the time cells are missing or in the wrong place, and if you scroll around the collection view sometimes the missing cells will appear or disappear. A lot of the time the missing cells are at the end of the view. Changing the estimatedSize makes a huge difference in how things work and how fast the view reloads, contrary to the doc, which implies it's just an optimization. My conclusion is that it's just too flakey to be useful beyond the most simple examples.

@Ramesh 2015-02-03 04:16:02

The wwdc 2014 "What's new in Table Views and Collection Views" talks about contentSizeAdjustment and contentSizeOffset to handle the disappearing cells, content size mis-match, offset issues, and scrolling bumpy-ness. However, it doesn't provide clear example on how to use those two variables. Hope this leads to solution :)

@Can Poyrazoğlu 2015-06-08 12:18:15

I've tried everything in this answer (and beyond) but still couldn't make it to work.

@Daniel Galasko 2015-06-08 12:33:18

@CanPoyrazoğlu try checkout my github project and build on that:)

@Can Poyrazoğlu 2015-06-08 12:35:26

@DanielGalasko I'm looking, but my collection view usually needs to look like table view, and as you've mentioned, it doesn't work right out of box for scenario.

@Daniel Galasko 2015-06-08 12:36:45

@CanPoyrazoğlu yes thats definitely one of the biggest limitations. its quite tricky to get working, i posted a question here about it stackoverflow.com/questions/28670951/…

@Can Poyrazoğlu 2015-06-08 12:37:26

@DanielGalasko I'll let you know if I find a solution.

@Shabarinath Pabba 2015-06-12 22:26:41

@DanielGalasko what if you have more than one xib for the cells? meaning three different layout cells? how would you implement the "calculateStandAlonecellHeight"? I tried everything in here except for the "calculateStandAloneCellHeight" the problem is if I "reloadData" in "Landscape" orientation and then switch back to portrait, somehow the new cells that appear after this rotation have a different size than what the "preferedLayoutAttributesFittingAttributes" return

@Daniel Galasko 2015-06-13 07:46:40

1: you need to make sure that your layout is invalidated when the bounds changes. Flow layout has an override for this. The same way you know what cell to dequeue in cellForRow is how you know what cell to use to calculate the height @ShabarinathPabba

@Sana 2015-06-22 20:49:28

I have a customcollectionview cell and this [self.contentView systemLayoutSizeFittingSize:(UILayoutFittingCompressedSize)]‌​.height is returning zero always. I am doing all the steps that has been suggested.

@Daniel Galasko 2015-06-23 05:31:13

@Sana then your constraints are most likely wrong. Please see my link about configuring constraints on a cell:)

@Juraj Antas 2015-08-18 15:06:29

I was just testing UICollectionView with dynamic type and autosizing cells is enabler for such type of functionality. Unfortunately it doesn't work correctly. Layout changes after cells are visible. This is also visible in demo provided. When data source provides more strings that are longer, result is completely broken. This is a correct answer how it should be done, but unfortunately UICollectionFlow layout autosizing is broken and can not be used in production :( This doesn't work correctly yet even on iOS9 beta5. Pity.

@Daniel Galasko 2015-08-19 12:33:15

@JurajAntas are you invalidating your layout? I haven't had any issues. Perhaps ask a new question?

@Juraj Antas 2015-08-19 12:50:41

@DanielGalasko Not sure what you are referring to, but your own demo is not working correctly. Used xcode7 beta5 and running on iOS8.4.1. Result produced is not satisfactory at all. Here is screen recording: youtu.be/2EQHgM6EdKA

@Wes Campaigne 2015-09-15 23:45:15

This appears to be fundamentally broken on iOS 9 GM. Setting estimatedItemSize leads to crashes -- there seems to be some huge bug in how auto layout is trying to process the UICollectionViewCell.

@Tony 2015-10-10 06:19:19

Running into a similar issue, has anybody found a work around?

@Daniel Galasko 2016-02-16 19:23:38

@WesCampaigne I updated my Github, your input would be appreciated. cc

@João Nunes 2016-04-05 13:09:55

I fixed this in ios 9 doing the following: no preferredLayoutAttributesFittingAttributes. Created my cell using initWithFrame (so no XIB/storyboard)

@Daniel Galasko 2016-04-05 13:14:01

@JoãoNunes that doesn't make sense. I never advocated storyboards or XIBs. What was broken on iOS 9? I would welcome a PR on GitHub if you feel it was broken

@João Nunes 2016-04-05 14:00:14

your solution as others say does not work always. Try changing the constraints of a cell after selection and deselection for ex. The whole collection breaks again. The Auto sizing is broken on ios9! I will revert back to use itemSize delegate and do the calculations myself.

@Daniel Galasko 2016-04-05 14:06:50

@JoãoNunes completely agree. It's impossible to get it working so I also use normal delegate size calculation. I thought you managed to get estimatedItemSize working. I know if you change the layout as well it crashes

@Matt Koala 2016-06-03 19:39:30

@DanielGalasko Check out my answer below. I wasn't originally able to comment against your answer but mine is a lot simpler, I recommend editing yours.

@Senseful 2016-07-04 23:31:41

Thanks for the post! I tried it, but ran into problems with a simple example. I think we might need to set contentView.translatesAutoresizingMaskToConstraints = false.

@Daniel Galasko 2016-07-05 04:53:41

@Senseful I am so close to turning this post into a Wiki. It's really tough to maintain because it's so buggy and difficult to get working!

@user3621075 2016-07-07 13:37:21

I find that simply implementing a "preferredLayoutAttributesFittingAttributes" in my custom UICollectionViewCell that simply returns the layout parameter fixes sizing issues on reused cells. This effectively disables the default version of the function, if I include a call to the super version, again the sizing fails.

@Bista 2016-09-01 11:42:18

Is there is any way that the cells can take up empty spaces between them just like game of tetris, if they have different height and width among them?

@kennyevo 2016-10-25 08:07:12

Hi, this works well if my collectionView is static, but if I add some cells (and it won't fit on the screen), the last cells are displayed only when I scroll. If I invalidate the flow's layout in the viewWillLayoutSubviews, it works well on ios10, but infinite loop on ios9, if I invalidate the layout after I reload the collectionView, it works well on ios10, but breaks on ios9 after adding a few cells, any idea?

@James Heald 2017-12-01 02:27:37

Is preferredLayoutAttributesFittingAttributes more performant than Auto-Layout? Ive tried an Auto Layout grid list before wiht varying item sizes and the performance was awful. Hoping to give your preferredLayoutAttributesFittingAttributes suggestion a try tomorrow and see what the results are.

@Daniel Galasko 2017-12-05 10:37:29

@JamesHeald I can't comment on performance as it depends on your implementation :) you will just need to profile it

@boog 2018-06-13 07:26:19

shouldn't we call super preferredLayoutAttributesFitting first?

@karenms 2017-08-23 04:50:12

  1. Add flowLayout on viewDidLoad()

    override func viewDidLoad() {
        super.viewDidLoad() 
        if let flowLayout = infoCollection.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(width: 1, height:1)
        }
    }
    
  2. Also, set an UIView as mainContainer for your cell and add all required views inside it.

  3. Refer to this awesome, mind-blowing tutorial for further reference: UICollectionView with autosizing cell using autolayout in iOS 9 & 10

@Ankush 2018-04-05 13:39:06

It worked like charm. Thanks man

@shoe 2017-07-27 21:35:58

I tried using estimatedItemSize but there were a bunch of bugs when inserting and deleting cells if the estimatedItemSize was not exactly equal to the cell's height. i stopped setting estimatedItemSize and implemented dynamic cell's by using a prototype cell. here's how that's done:

create this protocol:

protocol SizeableCollectionViewCell {
    func fittedSize(forConstrainedSize size: CGSize)->CGSize
}

implement this protocol in your custom UICollectionViewCell:

class YourCustomCollectionViewCell: UICollectionViewCell, SizeableCollectionViewCell {

    @IBOutlet private var mTitle: UILabel!
    @IBOutlet private var mDescription: UILabel!
    @IBOutlet private var mContentView: UIView!
    @IBOutlet private var mTitleTopConstraint: NSLayoutConstraint!
    @IBOutlet private var mDesciptionBottomConstraint: NSLayoutConstraint!

    func fittedSize(forConstrainedSize size: CGSize)->CGSize {

        let fittedSize: CGSize!

        //if height is greatest value, then it's dynamic, so it must be calculated
        if size.height == CGFLoat.greatestFiniteMagnitude {

            var height: CGFloat = 0

            /*now here's where you want to add all the heights up of your views.
              apple provides a method called sizeThatFits(size:), but it's not 
              implemented by default; except for some concrete subclasses such 
              as UILabel, UIButton, etc. search to see if the classes you use implement 
              it. here's how it would be used:
            */
            height += mTitle.sizeThatFits(size).height
            height += mDescription.sizeThatFits(size).height
            height += mCustomView.sizeThatFits(size).height    //you'll have to implement this in your custom view

            //anything that takes up height in the cell has to be included, including top/bottom margin constraints
            height += mTitleTopConstraint.constant
            height += mDescriptionBottomConstraint.constant

            fittedSize = CGSize(width: size.width, height: height)
        }
        //else width is greatest value, if not, you did something wrong
        else {
            //do the same thing that's done for height but with width, remember to include leading/trailing margins in calculations
        }

        return fittedSize
    }
}

now make your controller conform to UICollectionViewDelegateFlowLayout, and in it, have this field:

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout {
    private var mCustomCellPrototype = UINib(nibName: <name of the nib file for your custom collectionviewcell>, bundle: nil).instantiate(withOwner: nil, options: nil).first as! SizeableCollectionViewCell
}

it will be used as a prototype cell to bind data to and then determine how that data affected the dimension that you want to be dynamic

finally, the UICollectionViewDelegateFlowLayout's collectionView(:layout:sizeForItemAt:) has to be implemented:

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    private var mDataSource: [CustomModel]

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

        //bind the prototype cell with the data that corresponds to this index path
        mCustomCellPrototype.bind(model: mDataSource[indexPath.row])    //this is the same method you would use to reconfigure the cells that you dequeue in collectionView(:cellForItemAt:). i'm calling it bind

        //define the dimension you want constrained
        let width = UIScreen.main.bounds.size.width - 20    //the width you want your cells to be
        let height = CGFloat.greatestFiniteMagnitude    //height has the greatest finite magnitude, so in this code, that means it will be dynamic
        let constrainedSize = CGSize(width: width, height: height)

        //determine the size the cell will be given this data and return it
        return mCustomCellPrototype.fittedSize(forConstrainedSize: constrainedSize)
    }
}

and that's it. Returning the cell's size in collectionView(:layout:sizeForItemAt:) in this way preventing me from having to use estimatedItemSize, and inserting and deleting cells works perfectly.

@HelloimDarius 2017-05-01 07:51:17

For anyone who tried everything without luck, this is the only thing that got it working for me. For the multiline labels inside cell, try adding this magic line:

label.preferredMaxLayoutWidth = 200

More info: here

Cheers!

@Jean Le Moignan 2016-12-09 19:16:34

To whomever it may help,

I had that nasty crash if estimatedItemSize was set. Even if I returned 0 in numberOfItemsInSection. Therefore, the cells themselves and their auto-layout were not the cause of the crash... The collectionView just crashed, even when empty, just because estimatedItemSize was set for self-sizing.

In my case I reorganized my project, from a controller containing a collectionView to a collectionViewController, and it worked.

Go figure.

@chengsam 2017-06-27 02:47:54

Try calling collectionView.collectionViewLayout.invalidateLayout() after collectionView.reloadData().

@Marosdee Uma 2018-03-29 08:33:56

for ios10 and below add this code ` override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() if #available(iOS 11.0, *) { } else { mainCollectionView.collectionViewLayout.invalidateLayout() } }`

@Yang Young 2016-10-18 14:54:33

Update more information:

  • If you use flowLayout.estimatedItemSize, suggest use iOS8.3 later version. Before iOS8.3, it will crash [super layoutAttributesForElementsInRect:rect];. The error message is

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

  • Second, in iOS8.x version, flowLayout.estimatedItemSize will cause different section inset setting did not work. i.e. function: (UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:.

@Yang Young 2016-09-19 04:03:06

If you implement UICollectionViewDelegateFlowLayout method:

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

When you call collectionview performBatchUpdates:completion:, the size height will use sizeForItemAtIndexPath instead of preferredLayoutAttributesFittingAttributes.

The rendering process of performBatchUpdates:completion will go through the method preferredLayoutAttributesFittingAttributes but it ignores your changes.

@dgatwood 2017-02-21 21:32:42

I've seen this misbehavior, but only when the estimated size is zero. Are you sure you're setting an estimated size?

@Yang Young 2016-05-06 10:54:17

I did a dynamic cell height of collection view. Here is git hub repo.

And, dig out why preferredLayoutAttributesFittingAttributes is called more than once. Actually, it will be called at least 3 times.

The console log picture : enter image description here

1st preferredLayoutAttributesFittingAttributes:

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c290e0> index path: (<NSIndexPath:    0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 57.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

The layoutAttributes.frame.size.height is current status 57.5.

2nd preferredLayoutAttributesFittingAttributes:

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c16370> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

The cell frame height changed to 534.5 as our expected. But, the collection view still zero height.

3rd preferredLayoutAttributesFittingAttributes:

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa403d516a0> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 477);

You can see the collection view height was changed from 0 to 477.

The behavior is similar to handle scroll:

1. Before self-sizing cell

2. Validated self-sizing cell again after other cells recalculated.

3. Did changed self-sizing cell

At beginning, I thought this method only call once. So I coded as the following:

CGRect frame = layoutAttributes.frame;
frame.size.height = frame.size.height + self.collectionView.contentSize.height;
UICollectionViewLayoutAttributes* newAttributes = [layoutAttributes copy];
newAttributes.frame = frame;
return newAttributes;

This line:

frame.size.height = frame.size.height + self.collectionView.contentSize.height;

will cause system call infinite loop and App crash.

Any size changed, it will validate all cells' preferredLayoutAttributesFittingAttributes again and again until every cells' positions (i.e frames) are no more change.

@user3246173 2016-01-11 20:05:38

The example method above does not compile. Here is a corrected version (but untested as to whether or not it works.)

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
{
    let attr: UICollectionViewLayoutAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes

    var newFrame = attr.frame
    self.frame = newFrame

    self.setNeedsLayout()
    self.layoutIfNeeded()

    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    newFrame.size.height = desiredHeight
    attr.frame = newFrame
    return attr
}

@noobular 2016-02-13 00:59:12

After struggling with this for some time, I noticed that resizing does not work for UITextViews if you don't disable scrolling:

let textView = UITextView()
textView.scrollEnabled = false

@Gurunathan 2018-08-28 09:54:43

when I try to scroll, the collection view cell is not smooth. Do you have any solution for this ?.

Related Questions

Sponsored Content

4 Answered Questions

[SOLVED] Auto Layout in UICollectionViewCell not working

26 Answered Questions

3 Answered Questions

5 Answered Questions

[SOLVED] iOS 8 auto-sizing UITableViewCell with UITextView

1 Answered Questions

5 Answered Questions

0 Answered Questions

10 Answered Questions

1 Answered Questions

[SOLVED] UICollectionView Cell Auto Size with AutoLayout iOS 8

Sponsored Content