ContentView не отступы в iOS 6 Ячейка прототипа UITableViewCell
Я настраиваю пользовательский UITableViewCell
, используя ячейку прототипа в Storyboard. Однако все элементы UILabel
(и другие элементы пользовательского интерфейса), похоже, не добавляются в ячейку contentView
, а добавляются непосредственно в представление UITableViewCell
. Это создает проблемы, когда ячейка помещается в режим редактирования, поскольку содержимое не изменяется автоматически/отступом (что бы оно делало, если они находились внутри contentView
).
Есть ли способ добавить элементы пользовательского интерфейса в contentView
при прокладке ячейки с помощью элементов интерфейса Builder/Storyboard/prototype? Единственный способ, который я нашел, - создать все в коде и использовать [cell.contentView addSubView:labelOne]
, что было бы не очень удобно, так как гораздо проще компоновать ячейку графически.
Ответы
Ответ 1
В ходе дальнейшего исследования (просмотр иерархии subview ячейки) Interface Builder помещает subviews в ячейку contentView
, он просто не похож на него.
Основной причиной проблемы стало автозапуск iOS 6. Когда ячейка находится в режиме редактирования (и отступом), contentView
также имеет отступы, поэтому вполне разумно, что все подпункты в contentView
будут перемещаться (отступ) из-за того, что они находятся в пределах contentView
. Однако все ограничения автоопределения, применяемые Interface Builder, по-видимому, относятся к самому UITableViewCell
, а не к contentView
. Это означает, что даже несмотря на то, что отступы contentView
, вложенные в него объекты не содержат ограничений, берут на себя ответственность.
Например, когда я поместил a UILabel
в ячейку (и положил ее на 10 точек с левой стороны ячейки), IB автоматически применил ограничение "Горизонтальное пространство (10)". Однако это ограничение относится к UITableViewCell
NOT contentView
. Это означает, что когда ячейка с отступом и движение contentView
перемещается, метка остается помещенной, поскольку она соблюдает ограничение, чтобы оставаться 10 точек с левой стороны UITableViewCell
.
К сожалению (насколько мне известно), нет способа удалить эти созданные IB ограничения изнутри самой IB, так вот как я решил проблему.
В подклассе UITableViewCell
для ячейки я создал IBOutlet
для этого ограничения, называемого cellLabelHSpaceConstraint
. Вам также понадобится 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 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
, поэтому, когда я исправил ограничение/позиционирование этого элемента, все остальные последовали примеру и правильно позиционировали. Однако, если у вас более сложный макет, вам может понадобиться сделать это и для других подзапросов.
Ответ 2
Как уже упоминалось, XCode Interface Builder скрывает UITableViewCell contentView. В действительности все элементы пользовательского интерфейса, добавленные в UITableViewCell, на самом деле являются областями содержимого.
На данный момент 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];
}
}
Ответ 3
Вот подкласс, основанный на других идеях ответов, я собираюсь настроить свои пользовательские ячейки на:
@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
Ответ 4
Альтернативой подклассу является пересмотр ограничений в 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];
Ответ 5
Я думаю, что это исправлено в iOS 7 beta 3, что делает ненужные обходные пути с этой точки (но, вероятно, безвредными, поскольку в большинстве случаев они станут пустыми операциями).
Ответ 6
Основанный на коде 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 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. Благодарю!!! Аль-кредиты идут к нему.