Autoresizing issue of UICollectionViewCell contentView's frame in Storyboard prototype cell (Xcode 6, iOS 8 SDK) happens when running on iOS 7 only

Objective CIos7Ios8Xcode6Uicollectionviewcell

Objective C Problem Overview


I'm using Xcode 6 Beta 3, iOS 8 SDK. Build Target iOS 7.0 using Swift. Please refer to my problem step by step with screenshots below.

I have a UICollectionView in Storyboard. 1 Prototype UICollectionViewCell which contains 1 label in the centre (no autoresizing rule). Purple background was to mark a contentView that is generated in runtime by the Cell I guess. That view will be resized properly base on my UICollectionViewLayoutDelegate eventually, but not on iOS 7. Notice that I'm using Xcode 6 and the problem only happens on iOS 7.

When I build the app on iOS 8. Everything is okay.

Note: Purple is the contentView, Blue is my UIButton with rounded corner.

http://i.stack.imgur.com/uDNDY.png

However, on iOS 7, all the subViews inside the Cell suddenly shrink to the frame of (0,0,50,50) and never conforms to my Autoresizing rule anymore.

http://i.stack.imgur.com/lOZH9.png

I assume this is a bug in iOS 8 SDK or Swift or maybe Xcode?


Update 1: This problem still exists in the official Xcode 6.0.1 ! The best work around is like what KoCMoHaBTa suggested below by setting the frame in cellForItem of the cell (You have to subclass your cell though). It turned out that this is a incompatibility between iOS 8 SDK and iOS 7 (check ecotax's answer below quoted from Apple).

Update 2: Paste this code at the beginning of your cellForItem and things should be okay:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/

Objective C Solutions


Solution 1 - Objective C

contentView is broken. It can be also fixed in awakeFromNib

ObjC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}

Solution 2 - Objective C

I encountered the same problem and asked Apple DTS for help. Their reply was:

> In iOS 7, cells’ content views sized themselves via autoresizing > masks. In iOS 8, this was changed, cells stopped using the > autoresizing masks and started sizing the content view in > layoutSubviews. If a nib is encoded in iOS 8 and then decode it on iOS > 7, you’ll have a content view without an autoresizing mask and no > other means by which to size itself. So if you ever change the frame > of the cell, the content view won’t follow. > > Apps being deploying back to iOS 7 will have to work around this by > sizing the content view itself, adding autoresizing masks, or adding > constraints.

I guess this means that it's not a bug in XCode 6, but an incompatibility between the iOS 8 SDK and iOS 7 SDK, which will hit you if you upgrade to Xcode 6, because it will automatically start using the iOS 8 SDK.

As I commented before, the workaround Daniel Plamann described works for me. The ones described by Igor Palaguta and KoCMoHaBTa look simpler though, and appear to make sense giving Apple DTS' answer, so I'll try those later.

Solution 3 - Objective C

I encountered the same issue and hope that Apple will fix this with the next Xcode version. Meanwhile I use a workaround. In my UICollectionViewCell subclass I've just overridden layoutSubviews and resize the contentView manually in case the size differs from collectionViewCell size.

- (void)layoutSubviews
{
  [super layoutSubviews];
 
  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);
  
  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}

Solution 4 - Objective C

Another solution is to set the contentView's size and autoresizing masks in -collectionView:cellForItemAtIndexPath: like the following:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    
     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

This works with Auto Layout too, since auto resizing masks are translated to constraints.

Solution 5 - Objective C

In Xcode 6.0.1 contentView for UICollectionViewCell is broken for iOS7 devices. It can be also fixed by adding proper constraints to UICollectionViewCell and its contentView in awakeFromNib or init methods.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;
        
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];

Solution 6 - Objective C

This will not work correctly without any of the other mentioned workarounds because of a bug in Xcode 6 GM with how Xcode compiles xib files into the nib format. While I cannot say for 100% certainty it is Xcode related and not having to do with runtime, I'm very confident - here's how I can show it:

  1. Build+Run the application in Xcode 5.1.
  2. Go to the simulator application's directory and copy the compiled .nib file for the xib you are having issues with.
  3. Build+Run the application in Xcode 6 GM.
  4. Stop the application.
  5. Replace the .nib file in the newly built application's simulator folder with the .nib file created using Xcode 5.1
  6. Relaunch the app from the simulator, NOT from Xcode.
  7. Your cell loaded from that .nib should work as expected.

I hope everyone who reads this question will file a Radar with Apple. This is a HUGE issue and needs addressing before the final Xcode release.

Edit: In light of ecotax's post, I just wanted to update this to say it is now confirmed behavior differences between building in iOS 8 vs iOS 7, but not a bug. My hack fixed the issue because building on iOS 7 added the autoresizing mask to the content view needed to make this work, which Apple no longer adds.

Solution 7 - Objective C

The answers in this post work, what I never understood is why it works.

First, there are two "rules":

  1. For views created programmatically (Ex. [UIView new]), the property translatesAutoresizingMaskIntoConstraints is set to YES
  2. Views created in interface builder, with AutoLayout enabled, will have the property translatesAutoresizingMaskIntoConstraints set to NO

The second rule does not seem to apply to top-level views for which you do not define constraints for. (Ex. the content view)

When looking at a Storyboard cell, notice that the cell does not have its contentView exposed. We are not "controlling" the contentView, Apple is.

Deep dive into storyboard source code and see how contentView cell is defined:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Now the cell's subviews (notice the translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

The contentView does not have it's translatesAutoresizingMaskIntoConstraints set to NO. Plus it lacks layout definition, maybe because of what @ecotax said.

If we look into the contentView, it does have an autoresizing mask, but no definition for it: <autoresizingMask key="autoresizingMask"/>

So there are two conclusions:

  1. contentView translatesAutoresizingMaskIntoConstraints is set to YES.
  2. contentView lacks definition of a layout.

This leads us to two solutions which have been talked about.

You can set the autoresizing masks manually in awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Or you can set the contentView translatesAutoresizingMaskIntoConstraints to NO in awakeFromNib and define constraints in - (void)updateConstraints.

Solution 8 - Objective C

This is the Swift version of @Igor's answer which is accepted and thanks for your nice answer mate.

First Goto your UICollectionViewCell Subclass and paste the following code as it is inside the class.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

By the way I am using Xcode 7.3.1 and Swift 2.3. Solution is tested on iOS 9.3 which is working flawlessly.

Thanks, Hope this helped.

Solution 9 - Objective C

In swift, place the following code in the collection view cell subclass:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}

Solution 10 - Objective C

I have found that there are also issues with contentView sizing in iOS 8. It tends to get laid out very late in the cycle, which can cause temporary constraint conflicts. To solve this, I added the following method in a category of UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

This method should be called after dequeuing the cell.

Solution 11 - Objective C

I fixed doing this:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

see: here

Solution 12 - Objective C

Just make sure you check the check box "Autoresize subviews" in the nib for that collection view cell. It will work fine on both iOS 8 and iOS 7.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionthkeenView Question on Stackoverflow
Solution 1 - Objective CIgor PalagutaView Answer on Stackoverflow
Solution 2 - Objective CecotaxView Answer on Stackoverflow
Solution 3 - Objective CDaniel PlamannView Answer on Stackoverflow
Solution 4 - Objective CKoCMoHaBTaView Answer on Stackoverflow
Solution 5 - Objective CSerJ_GView Answer on Stackoverflow
Solution 6 - Objective CAceyView Answer on Stackoverflow
Solution 7 - Objective CkgaidisView Answer on Stackoverflow
Solution 8 - Objective ConCompletionView Answer on Stackoverflow
Solution 9 - Objective CIanView Answer on Stackoverflow
Solution 10 - Objective CphatmannView Answer on Stackoverflow
Solution 11 - Objective CSerlucaView Answer on Stackoverflow
Solution 12 - Objective CDeepak G MView Answer on Stackoverflow