我正在使用Storyboard中的原型单元配置自定义UITableViewCell
。但是,所有UILabel
s(和其他UI元素)似乎都没有添加到单元格contentView
中,而是直接添加到UITableViewCell
视图中。当单元格进入编辑模式时,这会产生问题,因为内容不会自动移位/缩进(如果它们位于contentView
内,它会这样做。)
在使用Interface Builder / Storyboard / prototype cell布置单元格时,有没有办法将UI元素添加到contentView
?我找到的唯一方法是在代码中创建所有内容并使用[cell.contentView addSubView:labelOne]
这不会很好,因为以图形方式布置单元格要容易得多。
答案 0 :(得分:66)
进一步调查(查看单元格的子视图层次结构)Interface Builder确实将子视图放在单元格的contentView
中,它看起来不像。
问题的根本原因是iOS 6自动布局。当单元格被置于编辑模式(并缩进)时,contentView
也会缩进,因此contentView
中的所有子视图都会因contentView
内的所有子视图移动(缩进)而被置于UITableViewCell
内。 1}}。但是,Interface Builder应用的所有自动布局约束似乎都与contentView
本身相关,而不是contentView
。这意味着即使UILabel
缩进,其中包含的子视图也不会 - 约束会受到影响。
例如,当我将UITableViewCell
放入单元格(并将其定位在距离单元格左侧10个点)时,IB会自动应用约束“水平空间(10)”。但是,此约束相对于contentView
而不是contentView
。这意味着当单元格缩进并且UITableViewCell
移动时,标签保持不变,因为它符合约束条件,从UITableViewCell
的左侧保留10个点。
不幸的是(据我所知),没有办法从IB本身中删除这些IB创建的约束,所以这就是我如何解决问题。
在单元格的IBOutlet
子类中,我为该约束创建了一个名为cellLabelHSpaceConstraint
的{{1}}。您还需要IBOutlet
作为标签本身,我称之为cellLabel
。然后,我按照以下方式实施了-awakeFromNib
方法:
- (void)awakeFromNib {
// -------------------------------------------------------------------
// We need to create our own constraint which is effective against the
// contentView, so the UI elements indent when the cell is put into
// editing mode
// -------------------------------------------------------------------
// Remove the IB added horizontal constraint, as that's effective
// against the cell not the contentView
[self removeConstraint:self.cellLabelHSpaceConstraint];
// Create a dictionary to represent the view being positioned
NSDictionary *labelViewDictionary = NSDictionaryOfVariableBindings(_cellLabel);
// Create the new constraint
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_cellLabel]" options:0 metrics:nil views:labelViewDictionary];
// Add the constraint against the contentView
[self.contentView addConstraints:constraints];
}
总之,上面将删除IB自动添加的水平间距约束(对UITableViewCell
而不是contentView
有效),然后我们定义并将自己的约束添加到{ {1}}。
在我的情况下,单元格中的所有其他contentView
都是根据UILabels
的位置定位的,因此当我修复此元素的约束/定位时,所有其他元素都会跟随并定位正确。但是,如果您的布局更复杂,那么您可能还需要为其他子视图执行此操作。
答案 1 :(得分:32)
如前所述,XCode的Interface Builder隐藏了UITableViewCell的contentView。实际上,添加到UITableViewCell的所有UI元素实际上都是contentView的子视图。
目前,它对布局约束没有做同样的魔术,这意味着它们都是在UITableViewCell级别表达的。
解决方法是在子类的awakeFromNib中将所有NSAutoLayoutConstrains从UITableViewCell移动到它的contentView并用contentView表达它们:
-(void)awakeFromNib{
[super awakeFromNib];
for(NSLayoutConstraint *cellConstraint in self.constraints){
[self removeConstraint:cellConstraint];
id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
NSLayoutConstraint* contentViewConstraint =
[NSLayoutConstraint constraintWithItem:firstItem
attribute:cellConstraint.firstAttribute
relatedBy:cellConstraint.relation
toItem:seccondItem
attribute:cellConstraint.secondAttribute
multiplier:cellConstraint.multiplier
constant:cellConstraint.constant];
[self.contentView addConstraint:contentViewConstraint];
}
}
答案 2 :(得分:9)
这是一个子类,基于其他答案的想法,我将基于我的自定义单元格:
@interface FixedTableViewCell ()
- (void)initFixedTableViewCell;
@end
@interface FixedTableViewCell : UITableViewCell
@end
@implementation FixedTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (nil != (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
[self initFixedTableViewCell];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self initFixedTableViewCell];
}
- (void)initFixedTableViewCell {
for (NSInteger i = self.constraints.count - 1; i >= 0; i--) {
NSLayoutConstraint *constraint = [self.constraints objectAtIndex:i];
id firstItem = constraint.firstItem;
id secondItem = constraint.secondItem;
BOOL shouldMoveToContentView = YES;
if ([firstItem isDescendantOfView:self.contentView]) {
if (NO == [secondItem isDescendantOfView:self.contentView]) {
secondItem = self.contentView;
}
}
else if ([secondItem isDescendantOfView:self.contentView]) {
if (NO == [firstItem isDescendantOfView:self.contentView]) {
firstItem = self.contentView;
}
}
else {
shouldMoveToContentView = NO;
}
if (shouldMoveToContentView) {
[self removeConstraint:constraint];
NSLayoutConstraint *contentViewConstraint = [NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant];
[self.contentView addConstraint:contentViewConstraint];
}
}
}
@end
答案 3 :(得分:6)
子类化的替代方法是修改cellForRowAtIndexPath中的约束。
将单元格的所有内容嵌入容器视图中。然后将前导和尾随约束指向cell.contentView而不是表视图单元格。
UIView *containerView = [cell viewWithTag:999];
UIView *contentView = [cell contentView];
//remove existing leading and trailing constraints
for(NSLayoutConstraint *c in [cell constraints]){
if(c.firstItem==containerView && (c.firstAttribute==NSLayoutAttributeLeading || c.firstAttribute==NSLayoutAttributeTrailing)){
[cell removeConstraint:c];
}
}
NSLayoutConstraint *trailing = [NSLayoutConstraint
constraintWithItem:containerView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeTrailing
multiplier:1
constant:0];
NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:containerView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeLeading
multiplier:1
constant:0];
[cell addConstraint:trailing];
[cell addConstraint:leading];
答案 4 :(得分:2)
我认为这在iOS 7 beta 3中已得到修复,从那时起就不再需要解决方法(但在大多数情况下它们可能会变成无效操作)。
答案 5 :(得分:1)
基于Skoota的代码(我是初学者,不知道你做了多少,但是工作很出色)我的建议是将所有东西放在边对边的容器视图中并添加以下内容:
在单元格的头文件中,我有以下IBOutlets:
@property (weak, nonatomic) IBOutlet UIView *container;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leftConstrain;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *rightConstrain;
在实现文件中,我在awakeFromNib中有以下内容:
// Remove the IB added horizontal constraint, as that's effective gainst the cell not the contentView
[self removeConstraint:self.leftConstrain];
[self removeConstraint:self.rightConstrain];
// Create a dictionary to represent the view being positioned
NSDictionary *containerViewDictionary = NSDictionaryOfVariableBindings(_container);
// Create the new left constraint (0 spacing because of the edge-to-edge view 'container')
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-0-[_container]" options:0 metrics:nil views:containerViewDictionary];
// Add the left constraint against the contentView
[self.contentView addConstraints:constraints];
// Create the new constraint right (will fix the 'Delete' button as well)
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"[_container]-0-|" options:0 metrics:nil views:containerViewDictionary];
// Add the right constraint against the contentView
[self.contentView addConstraints:constraints];
再一次,Skoota使上述成为可能。谢谢!!! Al积分归他所有。