Auto layout constraints issue on iOS7 in UITableViewCell

62,789

Solution 1

I had this problem as well.. It appears that the contentView's frame doesn't get updated until layoutSubviews is called however the frame of the cell is updated earlier leaving the contentView's frame set to {0, 0, 320, 44} at the time when the constraints are evaluated.

After looking at the contentView in more detail, It appears that autoresizingMasks are no longer being set.

Setting the autoresizingMask before you constrain your views can resolve this issue:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        [self loadViews];
        [self constrainViews];
    }
    return self;
}

Swift5, ios14:

contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]

Solution 2

Apparently there is something wrong with UITableViewCell and UICollectionViewCell on iOS 7 using iOS 8 SDK.

You can update cell's contentView when the cell is reused like this:

For Static UITableViewController:

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }

    //your code goes here

    return cell;
}

#endif
#endif

Since Static Table View Controllers are fragile and can easy be broken if you implement some datasource or deletegate methods - there are checks that will ensure that this code will only be compiled and run on iOS 7

It is similar for standard dynamic UITableViewController:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    //your code goes here       
    return cell;
}

For this case we don't need the compilation extra check, since implementing this method is required.

The idea is the same for both cases and for UICollectionViewCell, like commented in this thread: Autoresizing issue of UICollectionViewCell contentView's frame in Storyboard prototype cell (Xcode 6, iOS 8 SDK) happens when running on iOS 7 only

Solution 3

As cells are being reused and the height can change based on the content, I think in general it would be better to set the priority of spacings to less than required.

In your case the UIImageView has a spacing of 15 to the top and the bottom view has a spacing of 0 to the bottom. If you set the priority of those constraints to 999 (instead of 1000), the app will not crash, because the constraints are not required.

Once the layoutSubviews method is called, the cell will have the correct height, and the constraints can be satisfied normally.

Solution 4

I still didn't find a good solution for storyboards... Some info also here: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

From what they advice there:

self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);

I've induced my solution:

  • right click the storyboard
  • Open As -> Source Code
  • search for string "44" there
  • it's gonna be like

.

<tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="44" ...
    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatMessageCell" id="bCG-aU-ivE" customClass="ChatMessageCell">
        <rect key="frame" x="0.0" y="22" width="320" height="44"/>

.

  • replace rowHeight="44" with rowHeight="9999" and height="44" with height="9999"
  • right click the storyboard
  • Open As -> Interface Builder
  • run your app and check the output

Solution 5

Just increase the default cell height. Looks like the problem is that the content of the cell is larger than the default (initial) cell size, violating some non-negativity constraints until the cell is resized to its actual size.

Share:
62,789

Related videos on Youtube

Alexis
Author by

Alexis

Senior #iOS Developer, #Swift lover, owner at firstsecond.eu, Android, software, geek, tech enthusiast, Star Wars, cashew nuts &amp; dinosaurus cookies

Updated on August 26, 2021

Comments

  • Alexis
    Alexis over 2 years

    I'm using auto layout constraints programmatically to layout my custom UITableView cells and I'm correctly defining the cell sizes in tableView:heightForRowAtIndexPath:

    It's working just fine on iOS6 and it does look fine in iOS7 as well

    BUT when I run the app on iOS7, here's the kind of message I see in the console:

    Break on objc_exception_throw to catch this in the debugger.
    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
    2013-10-02 09:56:44.847 Vente-Exclusive[76306:a0b] Unable to simultaneously satisfy constraints.
        Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
    (
            "<NSLayoutConstraint:0xac4c5f0 V:|-(15)-[UIImageView:0xac47f50]   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
            "<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
            "<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>",
            "<NSLayoutConstraint:0xac43680 V:[UIView:0xac4d0f0(1)]>",
            "<NSLayoutConstraint:0xac436b0 V:[UIView:0xac4d0f0]-(0)-|   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
            "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>
    

    And indeed there's one of the constraint in that list I don't want :

    "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
    

    and I cannot set the translatesAutoresizingMaskIntoConstraints property of the contentView to NO => it would mess up the entire cell.

    44 is the default cell height but I defined my custom heights in the table view delegate so why does the cell contentView has this constraint? What could cause this?

    In iOS6 it's not happening and everything looks just fine on both iOS6 and iOS7.

    My code is quite big so I won't post it here but feel free to ask for a pastebin if you need it.

    To specify how I'm doing it, on cell intialization:

    • I create all my labels, buttons, etc
    • I set their translatesAutoresizingMaskIntoConstraints property to NO
    • I add them as subviews of the contentView of the cell
    • I add the constraints on the contentView

    I'm also deeply interested in understanding why this happens only on iOS7.

    • Mike Pollard
      Mike Pollard over 10 years
      What is your custom cell height set to?
    • Alexis
      Alexis over 10 years
      my custom cell height is set to 90
    • jrturton
      jrturton over 10 years
      A colleague had the same issue yesterday, but with the default width being 320 (for an iPad app).
    • smileyborg
      smileyborg over 10 years
      There is a sample project here demonstrating this same issue: github.com/Alex311/TableCellWithAutoLayout Here are some of my observations on it: github.com/Alex311/TableCellWithAutoLayout/commit/…
    • Steve Moser
      Steve Moser about 10 years
      I ran into the same error but it was because I never initialized the extra prototype cell I was using to find the height of my custom cell.
    • testing
      testing over 9 years
      Is this issue a desired behavior or a bug? If it's a bug it won't get fixed because iOS 8 is out there (where the problem doesn't occur).
    • Alfy
      Alfy over 7 years
      May be duplicate to the stackoverflow.com/questions/16039845/…. And I have posted the answer there.
  • Alexis
    Alexis over 10 years
    Indeed my cells are taller than the default size but how does it happen that the default size is used since the tableview's delegate implement tableView:heightForRowAtIndexPath: ?
  • Vadim Yelagin
    Vadim Yelagin over 10 years
    Probably cells are created with the default size and then resized to the actual size.
  • Alexis
    Alexis over 10 years
    indeed it does the job, but why doesn't it happen on iOS6 then?
  • Alexis
    Alexis over 10 years
    This does the job thanks man. What could happen when using editing styles?
  • liamnichols
    liamnichols over 10 years
    @jafar No problem.. I haven't had time to look into it properly yet, but I noticed some strange behaviour showing the delete button... Not 100% sure if its related to setting the autoresizingMask though as my cells wouldn't load without applying it
  • Kevin Wadera
    Kevin Wadera over 10 years
    Didn't have any issue in any editing style either. Though I had different constraint set for each editing styles, it worked just fine, without those damned breaking constraint break logs
  • liamnichols
    liamnichols over 10 years
    @KevinWadera Yeah.. I reviewed my code again and realised it was in fact an issue with the backgroundView (totally unrelated).. I've edited my answer now removing that comment
  • smileyborg
    smileyborg over 10 years
    I don't think the autoresizing mask for the contentView is supposed to be flexible width or height -- I think it is supposed to be fixed in both dimensions, and set to exactly equal the size of its table view cell. That would suggest you don't really want to mess with it, so I would recommend this solution: stackoverflow.com/a/19135983/796419
  • smileyborg
    smileyborg over 10 years
    @jafar iOS 7 changed a lot of things around table view cells. On iOS 7, there is now a scroll view (of type UITableViewCellScrollView) in between the table view cell and the contentView; that likely explains the difference between iOS 6 and 7 here.
  • liamnichols
    liamnichols over 10 years
    @smileyborg That solution wouldn't work for me, I had issues with the width being the wrong size as my layout required > 320pts causing constraint issues and as far as I know, you can't set a default width.
  • smileyborg
    smileyborg over 10 years
    @L14M333 See the code I posted in this comment here -- basically, you should just be able to set self.contentView.bounds = CGRectMake(0, 0, 99999, 99999); before you add your constraints, which should resolve the issue.
  • catamphetamine
    catamphetamine over 10 years
    the autoresizingMask doesn't work for me: the initial constraint is gone but the 3 new are added "<NSAutoresizingMaskLayoutConstraint:0x8b79740 h=-&- v=-&- UITableViewCellContentView:0x8b44010.height == UITableViewCellScrollView:0x8b4a130.height>", "<NSAutoresizingMaskLayoutConstraint:0x8b7b9f0 h=-&- v=-&- UITableViewCellScrollView:0x8b4a130.height == LibraryCell:0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint:0x8b7c590 h=--& v=--& V:[LibraryCell:0x8ac19d0(0)]>"
  • liamnichols
    liamnichols over 10 years
    @KevinWadera are you using storyboards/interface builder
  • mokagio
    mokagio almost 10 years
    Works for me too, I've been struggling for like one hour on it! Thanks! <3
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 9 years
    From Apple's docs: You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
  • xarly
    xarly over 9 years
    This is the best answer, or at least the best solution for me. My Cell (Autolayout rules) worked great in iOS8 with XCode6 and worked great with iOS7 and 6 with XCode 5. I update XCode to XCode6 and it's no longer working in iOS6 and 7. So, thanks!!!!
  • onmyway133
    onmyway133 over 9 years
    This is no longer a problem with Xcode 6.1. Also, do not use NSFoundationVersionNumber, please see nshipster.com/swift-system-version-checking
  • testing
    testing over 9 years
    After getting rid of other auto layout problems (iOS 7/iOS 8) I found out that using self.contentView.bounds = CGRectMake(0, 0, 99999, 99999); works as good as using self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleW‌​idth;. I put this into updateConstraints before adding the constraints to my content view.
  • testing
    testing over 9 years
    @kuchumovn: I think I had the same problem like you. I solved it by returning a height for each row as stated here. You can see that clearly because the one problem concerns the contentView (like in this SO question) and yours the height of the table view cell V:[LibraryCell:0x8ac19d0(0)]. Mine had the default height of 44. Don't know why you got 0.
  • testing
    testing over 9 years
    @onmyway133: Why is this not a problem anymore in Xcode 6.1? I still have the problem even if I use Xcode 6.1 and iOS 8.1 SDK. The problem only occurs on iOS 7. Changing the bounds or setting an autoresizing mask solves the problem. But I used the approach stated in the most voted answer or in the comments thereof.
  • Golden Thumb
    Golden Thumb over 9 years
    Yes this is the best answer based on my 3-hour googling. Other similar "solutions" don't provide the complete list of cell.contentView.autoresizingMask. Only this one works for my iPad 7.1 project created in Xcode 6.
  • doogilasovich
    doogilasovich over 9 years
    I was able to simply set the autoresizing mask in my awakeFromNib method and it fixed the issue for me.
  • Robert J. Clegg
    Robert J. Clegg over 9 years
    This helped me with an issue i had on iOS 7 and UICollectionViewCell width not expending the content as it should. Thanks!
  • Atharva
    Atharva about 9 years
    This worked for me. I had created a UITableViewController in storyboard where some cells were created from prototype and others entirely in code by instantiating a class.
  • Ryan Copley
    Ryan Copley about 9 years
    I want to start a religion praying to you. I've been searching for HOURS trying to fix this issue, and this resolves it (as well as setting the contentView.bounds to CGRectMake(0, 0, 99999, 99999); ) -- THIS IS A BUG IN APPLES CODE. So, I respectfully disagree with apples docs saying do not call that function. Apple, fix your crap.
  • Blacky
    Blacky almost 9 years
    You are a genius! nice and clean solution thank you :)
  • B-Rad
    B-Rad over 7 years
    It appears this issue has been fixed in OS 10, at least. When I run a simulator using 8.1 I get the warnings in the console but the same view controller does not generate warnings when the simulator is set to 10.
  • liamnichols
    liamnichols over 7 years
    @B-Rad I remember seeing something about view's having their frames set to 1000x1000 in iOS 10 and that might be why. Although I've not checked myself so it could be totally unrelated