【问题标题】:How to create a custom UITableViewCell programmatically using AutoLayout如何使用 AutoLayout 以编程方式创建自定义 UITableViewCell
【发布时间】:2013-09-28 22:41:58
【问题描述】:

我正在尝试实现一个 UITableView,它的行为类似于 twitter 客户端的时间线。现在我只是试图在 UITableViewCell 中获取两个标签。正如this Stack Overflow answer 所推荐的那样,我为每个布局使用了不同的reuseIdentifier。我的布局很简单,由一个标签或两个标签组成。最终我将调整 UITableViewCells 的高度,但首先我需要让单元格填充内容。

如果我用initWithFrame: 设置它们的框架,我可以得到标签,所以显示出来,但是约束没有被实现。

  • 问题:是什么阻止了标签和约束的出现?我在 UITableViewCell 的实现中显然遗漏了一些东西,但我不知道它是什么。

  • 第二个问题:我是否为viewDidLoad 中的每个reuseIdentifier 正确注册了UITableViewCell 类?

这可能会让人觉得很困难,但 Interface Builder 让我感到困惑,我想在代码中完成这一切。

下面是名为 TVTCell.h 的自定义 UITableViewCell 的代码:

static NSString * const kCellIDTitle = @"CellWithTitle";
static NSString * const kCellIDTitleMain = @"CellWithTitleMain";

@interface TVTCell : UITableViewCell
{
    NSString *reuseID;
}

@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UILabel *mainLabel;

@end

还有 TVTCell.m:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        reuseID = reuseIdentifier;

        nameLabel = [[UILabel alloc] init];
        [nameLabel setTextColor:[UIColor blackColor]];
        [nameLabel setBackgroundColor:[UIColor colorWithHue:32 saturation:100 brightness:63 alpha:1]];
        [nameLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:18.0f]];
        [nameLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self.contentView addSubview:nameLabel];

        mainLabel = [[UILabel alloc] init];
        [mainLabel setTextColor:[UIColor blackColor]];
        [mainLabel setBackgroundColor:[UIColor colorWithHue:66 saturation:100 brightness:63 alpha:1]];
        [mainLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:18.0f]];
        [mainLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self.contentView addSubview:mainLabel];

        [self.contentView setTranslatesAutoresizingMaskIntoConstraints:NO];

    }
    return self;
}


- (void)updateConstraints
{
    [super updateConstraints];

    NSDictionary *views = NSDictionaryOfVariableBindings(nameLabel, mainLabel);
    if (reuseID == kCellIDTitle) {
        NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameLabel]|"
                                                options: NSLayoutFormatAlignAllCenterX
                                                metrics:nil
                                                  views:views];
        [self.contentView addConstraints:constraints];
        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameLabel]|"
                                                              options: NSLayoutFormatAlignAllCenterX
                                                              metrics:nil
                                                                views:views];
        [self.contentView addConstraints:constraints];
    }
    if (reuseID == kCellIDTitleMain) {
        NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameLabel]|"
                                                                       options: NSLayoutFormatAlignAllCenterX
                                                                       metrics:nil
                                                                         views:views];
        [self.contentView addConstraints:constraints];

        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[mainLabel]|"
                                                                       options: NSLayoutFormatAlignAllCenterX
                                                                       metrics:nil
                                                                         views:views];
        [self.contentView addConstraints:constraints];

        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameLabel][mainLabel]|"
                                                              options: NSLayoutFormatAlignAllLeft
                                                              metrics:nil
                                                                views:views];
        [self.contentView addConstraints:constraints];

        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:nameLabel
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:0.0
                                      constant:44.0]];
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:nameLabel
                                                                     attribute:NSLayoutAttributeWidth
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:self.contentView
                                                                     attribute:NSLayoutAttributeNotAnAttribute
                                                                    multiplier:0.0
                                                                      constant:1]];
    }
}

抱歉,代码太多了。这是我的 UITableView 的tableView:cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0 || indexPath.row == 2 || indexPath.row == 3) {
        TVTCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIDTitle forIndexPath:indexPath];

        [[cell nameLabel] setText:[nameArray objectAtIndex:indexPath.row]];

        return cell;
    } else if (indexPath.row == 1 || indexPath.row == 4 || indexPath.row == 5) {
        TVTCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIDTitleMain forIndexPath:indexPath];

        [[cell nameLabel] setText:[nameArray objectAtIndex:indexPath.row]];
        [[cell mainLabel] setText:[dataArray objectAtIndex:indexPath.row]];

        return cell;
    } else
    {
        UITableViewCell *badCell = [[UITableViewCell alloc] init];
        NSLog(@"Warning! returning a cell that shouldnt be here");
        badCell.textLabel.text = @"Warning!";
        return badCell;
    }
}

最后,UITableView 的 viewDidLoad 方法:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[self tableView] registerClass:[TVTCell class] forCellReuseIdentifier:kCellIDTitle];
    [[self tableView] registerClass:[TVTCell class] forCellReuseIdentifier:kCellIDTitleMain];
}

【问题讨论】:

  • 我发现了同样的困惑。谢谢你的提问。

标签: ios objective-c uitableview constraints autolayout


【解决方案1】:

您可以在 swift 4 中使用自动布局以编程方式创建 UITableViewCell,如下所示。这不是您在问题中指定的上述问题的解决方案,它是更通用的实现如何使用自动布局以编程方式快速创建 Tableview 单元格:

class ViewController: UITableViewController {

override func viewDidLoad() {
   super.viewDidLoad()
   tableView.register(CustomCell2.self, forCellReuseIdentifier: "cell")
}

override func numberOfSections(in tableView: UITableView) -> Int {
  return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return 2
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? CustomCell2 else { return UITableViewCell() }
  cell.model = CellModel(labelString: "set constriant by code")
  return cell
  }  
  }

定义模型:

struct CellModel {
  let labelString : String
 }

定义自定义单元格:

class CustomCell2 : UITableViewCell {
private let label : UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false // enable auto layout
    label.backgroundColor = .green // to visualize the background of label
    label.textAlignment = .center // center text alignment
    return label
}()

private func addLabel() {
    addSubview(label)
    NSLayoutConstraint.activate([
        // label width is 70% of cell width 
        label.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.7),
        // label is horizontally center of cell
        label.centerXAnchor.constraint(equalTo: centerXAnchor)
    ])
}

var model : CellModel? {
    didSet {
        label.text = model?.labelString ?? ""
    }
}

// Init 
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    addLabel()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
}

This is the output of above program.

This is the actual project, you can check out.

【讨论】:

    【解决方案2】:

    您的代码有几个问题。首先,我想你会发现,如果你做一些记录,updateConstraints 永远不会被调用。我会将所有代码放在 init 方法中。此外,您的约束中有几处错误。不需要将高度设置为 44 的约束,因为您已经将标签固定到单元格的到和底部。我不知道你想用最后一个做什么,看起来这会使 nameLabel 宽 1 点。此外,您不应该将内容视图的 translatesAutoresizingMaskIntoConstraints 设置为 NO,这会导致奇怪的效果。所以这是我认为你想要的代码:

    - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
        self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
        if (self) {
            reuseID = reuseIdentifier;
    
            nameLabel = [[UILabel alloc] init];
            [nameLabel setTextColor:[UIColor blackColor]];
            [nameLabel setBackgroundColor:[UIColor colorWithHue:32 saturation:100 brightness:63 alpha:1]];
            [nameLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:18.0f]];
            [nameLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
            [self.contentView addSubview:nameLabel];
    
            mainLabel = [[UILabel alloc] init];
            [mainLabel setTextColor:[UIColor blackColor]];
            [mainLabel setBackgroundColor:[UIColor colorWithHue:66 saturation:100 brightness:63 alpha:1]];
            [mainLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:18.0f]];
            [mainLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
            [self.contentView addSubview:mainLabel];
    
            NSDictionary *views = NSDictionaryOfVariableBindings(nameLabel, mainLabel);
            if (reuseID == kCellIDTitle) {
                NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameLabel]|"
                                                                               options: 0
                                                                               metrics:nil
                                                                                 views:views];
                [self.contentView addConstraints:constraints];
                constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameLabel]|"
                                                                      options: 0
                                                                      metrics:nil
                                                                        views:views];
                [self.contentView addConstraints:constraints];
            }
            if (reuseID == kCellIDTitleMain) {
                NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameLabel]|"
                                                                               options:0
                                                                               metrics:nil
                                                                                 views:views];
                [self.contentView addConstraints:constraints];
    
                constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[mainLabel]|"
                                                                      options: 0
                                                                      metrics:nil
                                                                        views:views];
                [self.contentView addConstraints:constraints];
    
                constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameLabel][mainLabel(==nameLabel)]|"
                                                                      options: 0
                                                                      metrics:nil
                                                                        views:views];
                [self.contentView addConstraints:constraints];
    
            }
        }
        return self;
    }
    

    【讨论】:

    • 太好了,感谢您更正我的代码。单元格显示良好,单标记单元格缩放以适合我的默认单元格高度 88.0,双标记单元格均匀共享空间。在我之前提到的 Stack Overflow 答案中,开发人员说:“让这些子视图的内在内容大小驱动表格视图单元格内容视图的高度,方法是确保垂直维度中的内容压缩阻力和内容拥抱约束每个子视图都不会被您添加的更高优先级的约束所覆盖。” 我将如何以及在哪里实现这一点?
    • @ShawnThroop,我不确定它是如何工作的——海报使用的方式与我通常使用的方式不同。您需要为您的细胞设置不同的高度吗?如果是这样,该高度是否仅取决于标签的数量,还是某些标签的行数可变?
    • 最终我的目标是一个高度主要取决于一个标签的数量或行数的单元格。我需要更多地阅读内容压缩阻力和内容拥抱。对于 Apple Docs ......顺便说一句,你通常会怎么做?我猜这比我要实现的更容易。
    • @ShawnThroop,我通常使用 sizeWithFont:constrainedToSize:lineBreakMode: 计算 heightForRowAtIndexPath 中单元格的高度:(现在已弃用,由 boundingRectWithSize:options:attributes:context: 代替)。由于我将标签固定在单元格的顶部和底部,因此标签会随单元格展开。
    • @ShawnThroop 抗压缩/拥抱约束是自动创建的低优先级,试图将视图保持在其固有内容大小。这个想法是通过显式添加将大小固定为某个静态值/等的必需优先级约束来让它们通过不覆盖它们来产生效果。所以你不需要做任何事情来让它们工作 - 只需将视图的边缘连接在一起,这样它们就会全部连接起来。只有当你过度约束并无意中覆盖了这些隐式约束时,你才会最终自取其辱。
    猜你喜欢
    • 2016-02-19
    • 1970-01-01
    • 2014-10-14
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 2011-02-02
    • 1970-01-01
    相关资源
    最近更新 更多