UITableViewCell 子类的奇怪布局约束
Strange layout constraints with UITableViewCell subclass
我刚刚遇到一个关于奇怪的 UITableViewCell 子类布局的奇怪问题。
我用代码中的所有这些东西配置了一个 UITableViewCell
子类。
- iPhone 5 秒及以下 工作完美
- iPhone 6 或加上 错误:(
我确定那是子类问题。
(lldb) po _tableView
<UITableView: 0x7fa5db054c00; frame = (0 0; 375 618); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fa5d96649e0>; layer = <CALayer: 0x7fa5d96632a0>; contentOffset: {0, 0}; contentSize: {375, 15552}>
代码:
@interface ColorCell : UITableViewCell
@end
@implementation ColorCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.background = [UIView new];
self.top = [UIView new];
self.bottom = [UIView new];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
self.background.translatesAutoresizingMaskIntoConstraints = NO;
self.top. translatesAutoresizingMaskIntoConstraints = NO;
self.bottom. translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.background];
[self.background addSubview:self.top];
[self.background addSubview:self.bottom];
self.top.backgroundColor = [UIColor purpleColor];
self.bottom.backgroundColor = [UIColor blackColor];
}
return self;
}
- (void)updateConstraints
{
[super updateConstraints];
if (!self.didSetupConstraints) {
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_firstColor,_secondColor,_thirdColor,_fourthColor,_fifthColor,_background,_top,_bottom,_title,_stars,_favourites);
NSString *format;
NSArray *constraintsArray;
NSDictionary *metrics = @{@"topHeight":@100.0, @"bottomHeight":@50};
format = @"V:|[_background]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
[self.contentView addConstraints:constraintsArray];
format = @"H:|[_background]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
[self.contentView addConstraints:constraintsArray];
format = @"V:|[_top(topHeight)][_bottom(bottomHeight)]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:metrics views:viewsDictionary];
[_background addConstraints:constraintsArray];
format = @"H:|[_bottom]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:viewsDictionary];
[_background addConstraints:constraintsArray];
format = @"H:|[_top]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:viewsDictionary];
[_background addConstraints:constraintsArray];
self.didSetupConstraints = YES;
}
}
谢谢。
编辑:
(lldb) po [[UIWindow keyWindow] recursiveDescription]
<UIWindow: 0x7fa91341ce00; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fa91341b290>; layer = <UIWindowLayer: 0x7fa91341c610>>
| <UILayoutContainerView: 0x7fa913703e70; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa91343f400>; layer = <CALayer: 0x7fa913702480>>
| | <UINavigationTransitionView: 0x7fa913621e80; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x7fa91361d7e0>>
| | | <UIViewControllerWrapperView: 0x7fa9159678d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fa91343f150>>
| | | | <UIView: 0x7fa913441190; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fa913443820>>
| | | | | <UITableView: 0x7fa913841400; frame = (0 0; 375 618); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fa91342f520>; layer = <CALayer: 0x7fa913435060>; contentOffset: {0, 0}; contentSize: {375, 15582}>
| | | | | | <UITableViewWrapperView: 0x7fa913445200; frame = (0 0; 375 618); gestureRecognizers = <NSArray: 0x7fa913440320>; layer = <CALayer: 0x7fa9134319d0>; contentOffset: {0, 0}; contentSize: {375, 618}>
| | | | | | | <ColorCell: 0x7fa913637230; baseClass = UITableViewCell; frame = (0 600; 375 150); autoresize = W; layer = <CALayer: 0x7fa9136375b0>>
| | | | | | | | <UITableViewCellContentView: 0x7fa913637630; frame = (0 0; 375 150); gestureRecognizers = <NSArray: 0x7fa913637960>; layer = <CALayer: 0x7fa913637700>>
| | | | | | | | | <UIView: 0x7fa9136379c0; frame = (0 0; 324 150); layer = <CALayer: 0x7fa913637a90>>
| | | | | | | | | | <UIView: 0x7fa913638730; frame = (0 0; 324 100); layer = <CALayer: 0x7fa913638800>>
| | | | | | | | | | | <UIView: 0x7fa913637ab0; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637b80>>
| | | | | | | | | | | <UIView: 0x7fa913637ba0; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637c70>>
| | | | | | | | | | | <UIView: 0x7fa913637c90; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637d60>>
| | | | | | | | | | | <UIView: 0x7fa913637d80; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637e50>>
| | | | | | | | | | | <UIView: 0x7fa913637e70; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637f40>>
| | | | | | | | | | <UIView: 0x7fa913638820; frame = (0 100; 324 50); layer = <CALayer: 0x7fa9136388f0>>
| | | | | | | | | | | <UILabel: 0x7fa913637f60; frame = (8 8; 240 34); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa913636920>>
| | | | | | | | | | | | <_UILabelContentLayer: 0x7fa9135b5ba0> (layer)
| | | | | | | | | | | <UIImageView: 0x7fa913638290; frame = (256 25; 17 0); userInteractionEnabled = NO; layer = <CALayer: 0x7fa913638390>>
| | | | | | | | | | | <UILabel: 0x7fa9136383b0; frame = (281 -100; 35 0); clipsToBounds = YES; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa913638510>>
奇怪点:
如果我删除了 self.contentView.translatesAutoresizingMaskIntoConstraints = NO
,它就完美了。但奇怪的问题是对提出的约束发出警告。
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:0x7fad5b9764e0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fad5b971a20(44)]>",
"<NSLayoutConstraint:0x7fad5b976fb0 V:|-(0)-[UIView:0x7fad5b971db0] (Names: '|':UITableViewCellContentView:0x7fad5b971a20 )>",
"<NSLayoutConstraint:0x7fad5b976db0 V:[UIView:0x7fad5b971db0]-(0)-| (Names: '|':UITableViewCellContentView:0x7fad5b971a20 )>",
"<NSLayoutConstraint:0x7fad5b9763c0 V:|-(0)-[UIView:0x7fad5b972b70] (Names: '|':UIView:0x7fad5b971db0 )>",
"<NSLayoutConstraint:0x7fad5b977730 V:[UIView:0x7fad5b972b70(100)]>",
"<NSLayoutConstraint:0x7fad5b976410 V:[UIView:0x7fad5b972b70]-(0)-[UIView:0x7fad5b972c60]>",
"<NSLayoutConstraint:0x7fad5b9777b0 V:[UIView:0x7fad5b972c60(50)]>",
"<NSLayoutConstraint:0x7fad5b977800 V:[UIView:0x7fad5b972c60]-(0)-| (Names: '|':UIView:0x7fad5b971db0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fad5b976410 V:[UIView:0x7fad5b972b70]-(0)-[UIView:0x7fad5b972c60]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
您不需要 self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
,因为它不是以编程方式创建的。此外,您需要在覆盖结束时而不是开始时调用 [super updateConstraints]
。
编辑以包括错误(来自下面的评论):这里发生的是 UIView-Encapsulated-Layout-Height 是为自调整单元格创建的 iOS 8 约束。创建单元格后,如果修改约束,您将收到此错误。有几种方法可以管理它;在这种情况下,无论如何我都会提到,由于您只需要添加一次约束,因此请尝试不在 updateConstraints 中而是在 initWithStyle:reuseIdentifier.
中添加约束
问题更微妙,iOS8 出现了一个新问题:在某些时候,您的 tableView 认为行高为 44,因此它创建了一个约束,将单元格高度设置为 44。之后您添加这些约束,但 tableView 不会重新计算高度,因此会出现错误。如果像@Rob 上面指出的那样,您将高度设置为 44 就可以了。它不会解决你的问题是一些约束告诉 tableView 高度应该是 44,但不管你应该将 estimatedRowHeight 设置为 150,因为这看起来是一个安全的估计,或者如果它们肯定都是 150,只需设置 rowHeight到那个。
我终于弄清楚了约束警告的原因。
我删除了 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
委托方法,警告消失了。
所以答案很明确:我不应该在 heightForRowAtIndexPath
中对 约束更新 做些什么。相反,我只是 return rowHeight,然后问题就解决了。
我刚刚遇到一个关于奇怪的 UITableViewCell 子类布局的奇怪问题。
我用代码中的所有这些东西配置了一个 UITableViewCell
子类。
- iPhone 5 秒及以下 工作完美
- iPhone 6 或加上 错误:(
我确定那是子类问题。
(lldb) po _tableView
<UITableView: 0x7fa5db054c00; frame = (0 0; 375 618); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fa5d96649e0>; layer = <CALayer: 0x7fa5d96632a0>; contentOffset: {0, 0}; contentSize: {375, 15552}>
代码:
@interface ColorCell : UITableViewCell
@end
@implementation ColorCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.background = [UIView new];
self.top = [UIView new];
self.bottom = [UIView new];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
self.background.translatesAutoresizingMaskIntoConstraints = NO;
self.top. translatesAutoresizingMaskIntoConstraints = NO;
self.bottom. translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.background];
[self.background addSubview:self.top];
[self.background addSubview:self.bottom];
self.top.backgroundColor = [UIColor purpleColor];
self.bottom.backgroundColor = [UIColor blackColor];
}
return self;
}
- (void)updateConstraints
{
[super updateConstraints];
if (!self.didSetupConstraints) {
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_firstColor,_secondColor,_thirdColor,_fourthColor,_fifthColor,_background,_top,_bottom,_title,_stars,_favourites);
NSString *format;
NSArray *constraintsArray;
NSDictionary *metrics = @{@"topHeight":@100.0, @"bottomHeight":@50};
format = @"V:|[_background]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
[self.contentView addConstraints:constraintsArray];
format = @"H:|[_background]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
[self.contentView addConstraints:constraintsArray];
format = @"V:|[_top(topHeight)][_bottom(bottomHeight)]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:metrics views:viewsDictionary];
[_background addConstraints:constraintsArray];
format = @"H:|[_bottom]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:viewsDictionary];
[_background addConstraints:constraintsArray];
format = @"H:|[_top]|";
constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:viewsDictionary];
[_background addConstraints:constraintsArray];
self.didSetupConstraints = YES;
}
}
谢谢。
编辑:
(lldb) po [[UIWindow keyWindow] recursiveDescription]
<UIWindow: 0x7fa91341ce00; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fa91341b290>; layer = <UIWindowLayer: 0x7fa91341c610>>
| <UILayoutContainerView: 0x7fa913703e70; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa91343f400>; layer = <CALayer: 0x7fa913702480>>
| | <UINavigationTransitionView: 0x7fa913621e80; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x7fa91361d7e0>>
| | | <UIViewControllerWrapperView: 0x7fa9159678d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fa91343f150>>
| | | | <UIView: 0x7fa913441190; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fa913443820>>
| | | | | <UITableView: 0x7fa913841400; frame = (0 0; 375 618); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fa91342f520>; layer = <CALayer: 0x7fa913435060>; contentOffset: {0, 0}; contentSize: {375, 15582}>
| | | | | | <UITableViewWrapperView: 0x7fa913445200; frame = (0 0; 375 618); gestureRecognizers = <NSArray: 0x7fa913440320>; layer = <CALayer: 0x7fa9134319d0>; contentOffset: {0, 0}; contentSize: {375, 618}>
| | | | | | | <ColorCell: 0x7fa913637230; baseClass = UITableViewCell; frame = (0 600; 375 150); autoresize = W; layer = <CALayer: 0x7fa9136375b0>>
| | | | | | | | <UITableViewCellContentView: 0x7fa913637630; frame = (0 0; 375 150); gestureRecognizers = <NSArray: 0x7fa913637960>; layer = <CALayer: 0x7fa913637700>>
| | | | | | | | | <UIView: 0x7fa9136379c0; frame = (0 0; 324 150); layer = <CALayer: 0x7fa913637a90>>
| | | | | | | | | | <UIView: 0x7fa913638730; frame = (0 0; 324 100); layer = <CALayer: 0x7fa913638800>>
| | | | | | | | | | | <UIView: 0x7fa913637ab0; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637b80>>
| | | | | | | | | | | <UIView: 0x7fa913637ba0; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637c70>>
| | | | | | | | | | | <UIView: 0x7fa913637c90; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637d60>>
| | | | | | | | | | | <UIView: 0x7fa913637d80; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637e50>>
| | | | | | | | | | | <UIView: 0x7fa913637e70; frame = (0 0; 0 0); layer = <CALayer: 0x7fa913637f40>>
| | | | | | | | | | <UIView: 0x7fa913638820; frame = (0 100; 324 50); layer = <CALayer: 0x7fa9136388f0>>
| | | | | | | | | | | <UILabel: 0x7fa913637f60; frame = (8 8; 240 34); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa913636920>>
| | | | | | | | | | | | <_UILabelContentLayer: 0x7fa9135b5ba0> (layer)
| | | | | | | | | | | <UIImageView: 0x7fa913638290; frame = (256 25; 17 0); userInteractionEnabled = NO; layer = <CALayer: 0x7fa913638390>>
| | | | | | | | | | | <UILabel: 0x7fa9136383b0; frame = (281 -100; 35 0); clipsToBounds = YES; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fa913638510>>
奇怪点:
如果我删除了 self.contentView.translatesAutoresizingMaskIntoConstraints = NO
,它就完美了。但奇怪的问题是对提出的约束发出警告。
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:0x7fad5b9764e0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fad5b971a20(44)]>",
"<NSLayoutConstraint:0x7fad5b976fb0 V:|-(0)-[UIView:0x7fad5b971db0] (Names: '|':UITableViewCellContentView:0x7fad5b971a20 )>",
"<NSLayoutConstraint:0x7fad5b976db0 V:[UIView:0x7fad5b971db0]-(0)-| (Names: '|':UITableViewCellContentView:0x7fad5b971a20 )>",
"<NSLayoutConstraint:0x7fad5b9763c0 V:|-(0)-[UIView:0x7fad5b972b70] (Names: '|':UIView:0x7fad5b971db0 )>",
"<NSLayoutConstraint:0x7fad5b977730 V:[UIView:0x7fad5b972b70(100)]>",
"<NSLayoutConstraint:0x7fad5b976410 V:[UIView:0x7fad5b972b70]-(0)-[UIView:0x7fad5b972c60]>",
"<NSLayoutConstraint:0x7fad5b9777b0 V:[UIView:0x7fad5b972c60(50)]>",
"<NSLayoutConstraint:0x7fad5b977800 V:[UIView:0x7fad5b972c60]-(0)-| (Names: '|':UIView:0x7fad5b971db0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fad5b976410 V:[UIView:0x7fad5b972b70]-(0)-[UIView:0x7fad5b972c60]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
您不需要 self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
,因为它不是以编程方式创建的。此外,您需要在覆盖结束时而不是开始时调用 [super updateConstraints]
。
编辑以包括错误(来自下面的评论):这里发生的是 UIView-Encapsulated-Layout-Height 是为自调整单元格创建的 iOS 8 约束。创建单元格后,如果修改约束,您将收到此错误。有几种方法可以管理它;在这种情况下,无论如何我都会提到,由于您只需要添加一次约束,因此请尝试不在 updateConstraints 中而是在 initWithStyle:reuseIdentifier.
中添加约束问题更微妙,iOS8 出现了一个新问题:在某些时候,您的 tableView 认为行高为 44,因此它创建了一个约束,将单元格高度设置为 44。之后您添加这些约束,但 tableView 不会重新计算高度,因此会出现错误。如果像@Rob 上面指出的那样,您将高度设置为 44 就可以了。它不会解决你的问题是一些约束告诉 tableView 高度应该是 44,但不管你应该将 estimatedRowHeight 设置为 150,因为这看起来是一个安全的估计,或者如果它们肯定都是 150,只需设置 rowHeight到那个。
我终于弄清楚了约束警告的原因。
我删除了 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
委托方法,警告消失了。
所以答案很明确:我不应该在 heightForRowAtIndexPath
中对 约束更新 做些什么。相反,我只是 return rowHeight,然后问题就解决了。