40

UITableViewCellストーリーボードのプロトタイプセルを使用してカスタムを構成しています。ただし、すべてのUILabels(およびその他のUI要素)がセルに追加されているようには見えずcontentView、代わりにUITableViewCellビューに直接追加されています。これにより、セルが編集モードになっているときに問題が発生します。これは、コンテンツが自動的にシフト/インデントされないためです(コンテンツが内部にある場合はそうなりますcontentView)。

contentViewInterface Builder /ストーリーボード/プロトタイプセルを使用してセルをレイアウトするときにUI要素を追加する方法はありますか?私が見つけた唯一の方法は、すべてをコードで作成して使用する[cell.contentView addSubView:labelOne]ことです。これは、セルをグラフィカルにレイアウトする方がはるかに簡単なので、あまり良くありません。

4

6 に答える 6

67

さらに調査すると(セルのサブビュー階層を表示)、Interface Builderはセル内にサブビューを配置しますが、そのcontentViewようには見えません。

この問題の根本的な原因は、iOS6の自動レイアウトでした。セルが編集モードになっている(そしてインデントされている)と、contentViewもインデントされます。したがって、内にあるために、内のすべてのサブビューcontentViewが移動(インデント)されるのは当然contentViewです。ただし、Interface Builderによって適用されるすべての自動レイアウト制約はUITableViewCell、ではなく、それ自体に関連しているように見えcontentViewます。contentViewこれは、インデントがあっても、その中に含まれるサブビューはそうではないことを意味します-制約が担当します。

たとえばUILabel、セルにaを配置すると(セルの左側から10ポイント配置すると)、IBは自動的に「水平スペース(10)」という制約を適用しました。ただし、この制約はUITableViewCellNOTに関連していcontentViewます。これは、セルがインデントされてcontentView移動しても、ラベルは、の左側から10ポイントを維持するという制約に準拠しているため、配置されたままになることを意味しUITableViewCellます。

残念ながら(私が知る限り)、これらのIBによって作成された制約をIB自体から削除する方法はないため、ここで問題を解決しました。

UITableViewCellセルのサブクラス内に、IBOutletという制約のforを作成しましたcellLabelHSpaceConstraintIBOutletまた、私が呼んだラベル自体にも必要です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ので、この要素の制約/配置を修正すると、他のすべてがそれに続き、正しく配置されました。ただし、より複雑なレイアウトを使用している場合は、他のサブビューに対してもこれを行う必要がある場合があります。

于 2012-09-30T01:16:25.650 に答える
33

前述のように、XCodeのInterfaceBuilderは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];
  }
}
于 2012-12-15T14:41:27.967 に答える
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
于 2012-11-23T11:51:23.077 に答える
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];
于 2012-10-23T02:33:13.670 に答える
2

これはiOS7ベータ3で修正されていると思います。その時点から回避策は不要になります(ただし、ほとんどの場合、空の操作になるため、おそらく無害です)。

于 2013-07-08T22:13:11.037 に答える
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によって可能になりました。ありがとう!!!アルクレジットは彼に行きます。

于 2013-09-11T08:16:47.093 に答える