contentView不能在iOS 6 UITableViewCell原型单元格中缩进
我正在使用Storyboard中的原型单元格来configuration自定义的UITableViewCell
。 但是,所有UILabel
(和其他UI元素)似乎都不会被添加到单元格的contentView
,而是直接添加到UITableViewCell
视图中。 这会在单元格进入编辑模式时产生问题,因为内容不会自动移动/缩进(如果它们位于contentView
内部,它会这样做)。
在使用Interface Builder / Storyboard / prototype单元格布局单元格时,是否有任何方法将UI元素添加到contentView
? 我发现的唯一方法是在代码中创build一切,并使用[cell.contentView addSubView:labelOne]
这不会很好,因为以graphics方式布局单元格要容易得多。
在进一步的调查中(查看单元格的子视图层次结构),Interface Builder确实将子视图放置在单元格的contentView
,它看起来并不像它。
问题的根源是iOS 6自动布局。 当单元格被放置到编辑模式(和缩进)时, contentView
也被缩进,所以它可以理解为contentView
中的所有子视图都将由于在contentView
而移动(缩进)。 但是,Interface Builder应用的所有自动布局约束似乎都是相对于UITableViewCell
本身,而不是contentView
。 这意味着,即使contentView
缩进,其中包含的子视图也不会 – 约束负责。
例如,当我将一个UILabel
放入单元格(并将它放置在离单元格左边10点的位置)时,IB会自动应用一个约束“Horizontal Space(10)”。 但是,这个约束是相对于UITableViewCell
不是contentView
。 这意味着当单元格缩进时, contentView
移动时,标签将保持放置状态,因为它符合约束从UITableViewCell
的左侧保持10个点。
不幸的是(据我所知),没有办法从IB内部删除这些由IB创build的约束,所以这里是我如何解决这个问题。
在单元格的UITableViewCell
子类中,我为该约束创build了一个名为cellLabelHSpaceConstraint
的IBOutlet
。 你也需要一个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
),然后我们将自己的约束添加到contentView
。
在我的情况下,单元格中的所有其他UILabels
都是基于cellLabel
的位置进行定位的,所以当我修复这个元素的约束/定位时,所有其他的都遵循正确的定位。 但是,如果您有更复杂的布局,那么您可能还需要为其他子视图执行此操作。
如上所述,XCode的Interface Builder隐藏了UITableViewCell的contentView。 实际上,添加到UITableViewCell中的所有UI元素实际上都是contentView的子视图。
目前来看,IB对布局约束并没有这么做,这意味着它们都是以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]; } }
这是一个基于其他答案思想的子类,我将基于我的自定义单元格:
@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
子类化的替代方法是修改cellForRowAtIndexPath中的约束。
将单元格的所有内容embedded容器视图中。 然后将前导和尾随约束指向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];
我认为这是固定在iOS 7testing版3,从这一点上使变通办法变得不必要(但可能无害,因为在大多数情况下,他们将成为空操作)。
基于Skoota的代码(我是一个初学者,不太了解你做了什么,但是出色的工作)我的build议是把你所有的东西放在一个边对边的容器视图中,并添加以下内容:
在单元格的头文件中,我有以下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学分去了他。