执行 -layoutSubviews 后仍需要自动布局,使用UITableViewCell子类。

115
我正在使用XCode 4.5和iOS 6开发一个带有自定义单元格的简单表视图的应用程序。在iOS 5及以下版本中已经完成了一百次以上,但由于新的自动布局系统,在这里遇到了很多麻烦。
我在IB中设置了表视图和原型单元格,添加了子视图并将它们连接为IBOutlets,然后设置代理和数据源。然而,现在每当从cellForRowAtIndexPath获取第一个单元格时,我会收到以下错误:
Assertion failure in -[ShopCell layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2372/UIView.m:5776
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. ShopCell's implementation of -layoutSubviews needs to call super.'
我没有在我的子类化单元格(ShopCell)中实现-layoutSubviews方法,即使我尝试这样做并添加super调用,仍然会收到相同的错误。如果我在IB中删除单元格中的子视图,并将其更改为标准UITableViewCell,则一切正常,尽管当然我留下了没有数据的单元格。
我几乎可以确定有些简单的东西我错过了,但找不到任何文档或指南来提示我做错了什么。感谢任何帮助。

如果使用自动布局,请在调试器区域尝试lldb [[UIWindow keyWindow] _autoLayoutTrace]。 - A-Live
3
你是不是在使用UIView代替UITableViewCell作为自定义单元格?我之前也遇到了同样的问题。我一开始使用UIView作为自定义单元格,并向其中添加子视图。后来改用UITableViewCell就解决了问题。 - user1705389
取消检查检视器中的autoLayout框,然后清理并运行。它一定会起作用。 - Nico
我曾经遇到过同样的问题,为此苦苦挣扎了一段时间。user1705389的解决方案帮助了我。对我来说,这个问题出现在一个原本是单元格的表视图页脚上,我忘记将单元格的超类从UITableViewCell更改为简单的UIView。 - ShayDavidson
我在iOS 7.1中遇到了这个错误,但在iOS 8中没有。 - onmyway133
显示剩余2条评论
31个回答

57

在代码中手动添加约束时,我遇到了同样的问题。在代码中,我正在执行以下操作:

{
    [self setTranslatesAutoresizingMaskIntoConstraints:YES];
    [self addSubview:someView];
    [self addSubview:someOtherView];
    [self addConstraint:...];
}

假设

从我所了解的情况来看,问题在于当您禁用translatesAutoresizingMaskIntoConstraints时,UITableViewCell开始使用自动布局并自然而然地失败,因为layoutSublayersForLayer的底层实现没有调用super。使用Hopper或其他工具的人可以确认这一点。由于您正在使用IB,您可能想知道为什么会出现这个问题...因为使用IB会自动禁用添加约束的视图的translatesAutoresizingMaskIntoConstraints(它将自动添加一个宽度和高度约束)。

解决方案

我的解决方案是将所有东西移到contentView中。

{
   [self.contentView addSubview:someView];
   [self.contentView addSubview:someOtherView];
   [self.contentView addConstraint:...];
}

我不确定这是否适用于 Interface Builder,但如果您将单元格上的所有内容移出(假设您在其上直接放置了某些内容),那么它应该可以工作。希望这能帮到您!


4
我还需要在每个我添加到contentView的子视图上添加"subview.translatesAutoresizingMaskIntoConstraints = NO"。 - Jay Peyer
5
这对我很有效。同时,请确保不要为UITableViewCell调用self.contentView.translatesAutoresizingMaskIntoConstraints = NO - Maurizio

53

显然,UITableViewCell的layoutSubviews实现没有调用super,在使用自动布局时会出现问题。我很想知道是否将以下类别放入项目中可以解决问题。它在测试项目中有所帮助。

#import <objc/runtime.h>
#import <objc/message.h>

@implementation UITableViewCell (FixUITableViewCellAutolayoutIHope)

+ (void)load
{
    Method existing = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method new = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

当我在表格单元上使用backgroundView时,出现了这个问题,因为它会作为子视图添加到单元格中(而大多数子视图应该添加到表格单元格的contentView中,这样通常更好)。

注意:似乎iOS 7中已经修复了这个错误。我能够删除这段代码,或者至少添加一个运行时检查,以便仅在iOS 6上运行时才执行。


6
出于同样的原因(iOS 6.1 b1),我不得不在UITableView上创建这样一个类别。 - Joshua J. McKinnon
对我来说奇怪的是,我的Storyboard中禁用了自动布局:http://stackoverflow.com/questions/21842987/uitableviewcell-subclass-layout-messed-up-in-autolayout-disabled-storyboard - Rivera
这对我没有用。冒着被标记为重复的风险,这是相同的问题还是不同的问题?http://pastebin.com/eG2mFgU5(带有UITextView的UITableViewCell子类) - William Entriken
5
iOS 7中是否有适用于TableHeaderView的类似解决方法?因为这个问题仍然存在。 - Softlion
1
这个很有效。当我试图将UIVIew子视图居中在UITableView中时,我遇到了这个问题。即使在iOS 7中也会发生断言。但在iOS 8中不会发生,所以他们一定解决了这个bug。 - Jordan H
显示剩余7条评论

33
我有同样的错误几个月了。但是我找到了问题所在。
当我创建一个IB文件时,已经添加了一个UIView。如果您使用此视图,则在禁用自动布局时应用程序不会崩溃(但存在其他问题)。当您使用自动布局时,您必须在对象库中选择正确的视图:UITableViewCell
实际上,您应该始终使用此项,因为所有子视图都添加到UITableViewCellcontentView中。
就这样。一切都会好起来。

这不应该被接受作为答案,因为问题并不是关于使用IB的实现,而且当你不使用IB时,这个问题也可能发生。如果你正在以编程方式处理你的视图,@PhilLoden的答案更可行。 - Eric
我不理解这个答案。有人能更清楚地解释一下吗?谢谢。 - hasan
我认为我说的没错。只需在界面构建器中的身份验证检查类属性就足够了吗?还是被添加的对象属于另一种类型,类属性稍后已更新?这也会导致问题吗? - hasan
@hasan83 你实际上可以返回这个单元格。一个UITableViewCell基本上是带有重用标识符的UIView。 - Arnaud

17

我曾经遇到自定义 UITableViewHeaderFooterView 和 xib 的问题。

在这里看到了一些答案,但是我发现,在我的自定义页脚视图类中实现 -layoutSubviews 方法能够解决这个问题:

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}

1
请注意,这可能会导致无限循环,最终出现EXC_BAD_ACCESSKERN_PROTECTION_FAILURE错误。 - mbi
@mbi 调用selflayoutIfNeeded不能触发layoutSubviews,因为没有任何东西可以标记self需要在此处重新布局。如果出现了这种情况,则是自定义布局代码中的错误。 - Sound Blaster

15

我看到这是由于修改了layoutSubviews实现中的约束条件导致的。将调用super的位置从方法开头移到末尾可以解决这个问题。


这对我有用。我有一个自定义的UICollectionViewCell,我正在layoutSubviews中进行格式化。有人知道为什么这个解决方案有效吗? - STANGMMX
@STANGMMX,A'sa Dickens的回答解释了为什么。 - Fábio Oliveira

15

我在iOS 7中遇到了相同的问题(iOS 8似乎已经解决了)。对于我来说,解决方案是在我的viewDidLayoutSubviews方法末尾调用[self.view layoutIfNeeded]


谢谢。这对我很有帮助。我昨天遇到了这个问题(在iOS 7上)。这对iOS 7有帮助。 - Alexander
@MaciejSwic请看我在顶部的回答。 - Sound Blaster
这对我有用!使用iOS 7.1和Swift。我正在viewDidLayoutSubviews中删除和添加约束。我删除了super调用,但它仍然不起作用,但是这个解决方案管用!给这只恐龙一片树叶! :) - jomafer
我也在使用iOS 7.1时成功了! - fdlr

14

我曾经遇到了同样的问题。问题出在我创建单元格Xib的方式上。我像平常一样创建了一个Xib,然后只是将默认的"UIView"类型更改为自定义的UITableViewCell类。正确的方法是先删除默认视图,然后将表格视图单元格对象拖动到Xib中。更多详细信息请参见:http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/


1
完美的洞察力!我会花很长时间才能意识到这一点,特别是如果我使用禁用了自动布局的UIView,我的应用程序不会崩溃。 - Guilherme
太棒了!请参考@Arnaud下面的回复。 - Lubbo

7

我曾经遇到过类似的问题,不是在UITableViewCell上,而是在UITableView本身上。因为它是谷歌搜索结果的首个,所以我将它发布在这里。事实证明,viewForHeaderInSection是问题所在。我创建了一个UITableViewHeaderFooterView并将translatesAutoresizingMaskIntoConstraints设置为NO。现在来到有趣的部分:

iOS 7:

// don't do this on iOS 7
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

如果我这样做,应用程序就会崩溃,并出现以下错误信息:
Auto Layout仍然需要执行-layoutSubviews。UITableView的-layoutSubviews实现需要调用super。
我原本以为你不能在表视图头部使用自动布局,而只能在子视图中使用。但实际上情况并非如此。 总之,在iOS7上不要禁用标题的自动调整大小掩码。否则它可以正常工作。
// you have to do this, otherwise you get an auto layout error
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

如果我不使用这个,我会得到以下输出:
“无法同时满足约束。”
对于iOS 8,您需要禁用标题的自动调整大小掩码。
不知道为什么它会表现出这种方式,但似乎苹果在iOS 8中修复了一些问题,自动布局在iOS 7和iOS 8上的工作方式有所不同。

伙计,你刚刚救了我的一天! - Marcin Małysz

7

我通过关闭自定义表格视图单元格的所有子视图的“自动布局”来解决了这个问题。

在自定义单元格的xib中,选择一个子视图并取消勾选文件检查器>界面生成器文档>使用自动布局。


4
我也做了同样的事情。但如果你想使用自动布局,这并不是一个解决方案。 - ajmccall

5
如上所述,当您在UITableView中创建视图时,必须删除默认创建的视图,并将UITableViewCell或UITableViewHeaderFooterView拖动为根视图。然而,如果您忘记了这一点,还有一种方法可以修复XIB。您需要在文本编辑器中打开XIB文件,在<view> 标签和其直接子标签中添加或更改属性translatesAutoresizingMaskIntoConstraintsYES,例如:

<view contentMode="scaleToFill" horizontalHuggingPriority="1000" id="1" translatesAutoresizingMaskIntoConstraints="YES" customClass="MXWCollapsibleTableHeaderView">


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接