UITableViewCell 中 iOS7上的自动布局约束问题

我用编程方式使用自动布局约束来布局我自定义的 UITableView 单元格,并且在 tableView:heightForRowAtIndexPath:中正确定义了单元格大小

它在 iOS6上运行良好,在 iOS7上也能运行 听着

但是当我在 iOS7上运行这个应用程序时,我在控制台上看到了这样的信息:

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]>

的确在这个列表中有一个我不想要的约束:

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

我不能将 contentViewtranslatesAutoresizingMaskIntoConstraints属性设置为 NO = > ,否则会搞乱整个单元格。

44是默认的单元格高度,但是我在表视图委托中定义了自定义高度,那么为什么单元格 contentView 有这个约束呢?是什么引起的?

在 iOS6中,这种情况并没有发生,在 iOS6和 iOS7中,一切看起来都很正常。

我的代码相当大,所以我不会张贴在这里,但随时要求粘贴剂,如果你需要它。

为了说明我是如何做到的,在单元格初始化方面:

  • 我创建我所有的标签,按钮等
  • 我将它们的 translatesAutoresizingMaskIntoConstraints属性设置为 NO
  • 我添加它们作为单元格 contentView的子视图
  • 我在 contentView上添加了约束

我也非常想知道为什么只有在 iOS7上才会出现这种情况。

62994 次浏览

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.

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]

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

I had the same problem. My solution based upon others above:

- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// initialize my stuff
[self layoutSubviews]; // avoid debugger warnings regarding constraint conflicts
}
return self;
}

Best, Alessandro

I'v also faced this problem and none of the suggestions helped. In my case I had size selection cell, and it contained collectionView inside (every collectionView cell contained full size image). Now, the cell size heigh was a little bit bigger (60) than the collectionView (50) and images inside it (50). Because of that collectionView view has align bottom to superview constraint with a value of 10. That case me a warning, and the only way to fix that was to make cell height the same as its collectionView.

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.

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

I ended up by not using UITableViewCell in designer at all... I create there custom view and add it to content view of cell programmatically... With some help categories there is also no code boilerplate...

If it works fine in iOS8,and get the warning in iOS7,you can search the storyboard source code, and find the right tableviewCell, then append the rect attribute after the tableviewCell line. Thanks to kuchumovn.

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>

Maybe set the view's priority large than 750 and less than 1000 can solve.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",