27

I want to add in the cellForRowAtIndexPath some views to my cell content view and for them constraints but nothing works. I have something like this:

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:imageView
                                  attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeLeft multiplier:1.0f constant:10.0f];

[cell.contentView addConstraint:constraint];

How should I do this?

Pradhyuman sinh
  • 3,936
  • 1
  • 23
  • 38
Bartosz Bialecki
  • 4,391
  • 10
  • 42
  • 64
  • Are you doing this with an image view that you've manually added to the cell? I would advise against using name of the existing `imageView` properties (e.g., `imageView`, `textLabel` and `detailTextLabel`) when adding your own controls. Or are you trying to do this on that existing `imageView`, which I wouldn't recommend, either. – Rob Dec 01 '13 at 22:52
  • No, this is only sample name, I used another name for image view. – Bartosz Bialecki Dec 01 '13 at 23:53

1 Answers1

50

A couple of observations:

  1. Your creation of this particular constraint is correct. Clearly, you can't just set the left constraint, but you need to specify all of the constraints that will unambiguously define the frame of the cell's subviews. For example, define not only the left (or leading) constraint, but also the top, bottom, and width constraints. Or define the left constraint plus the the width and height constraints, and specify the vertical y constraint. Lots of different ways to do it, but the key is that you have to add all of the constraints that will unambiguously define the frame of all of the subviews.

    For example, you might have something like the following:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *cellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    
        UIImageView *customImageView;
        UILabel *customLabel;
    
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    
            customImageView = [[UIImageView alloc] init];
            customImageView.translatesAutoresizingMaskIntoConstraints = NO;
            customImageView.tag = IMAGEVIEWTAG;
            [cell.contentView addSubview:customImageView];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView
                                                                         attribute:NSLayoutAttributeLeading
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:cell.contentView
                                                                         attribute:NSLayoutAttributeLeft
                                                                        multiplier:1.0
                                                                          constant:25.0]];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView
                                                                         attribute:NSLayoutAttributeWidth
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:nil
                                                                         attribute:NSLayoutAttributeNotAnAttribute
                                                                        multiplier:1.0
                                                                          constant:30.0]];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView
                                                                         attribute:NSLayoutAttributeTop
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:cell.contentView
                                                                         attribute:NSLayoutAttributeTop
                                                                        multiplier:1.0
                                                                          constant:3.0]];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView
                                                                         attribute:NSLayoutAttributeBottom
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:cell.contentView
                                                                         attribute:NSLayoutAttributeBottom
                                                                        multiplier:1.0
                                                                          constant:-3.0]];
    
            customLabel = [[UILabel alloc] init];
            customLabel.translatesAutoresizingMaskIntoConstraints = NO;
            customLabel.tag = LABELTAG;
            [cell.contentView addSubview:customLabel];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel
                                                                         attribute:NSLayoutAttributeLeading
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:customImageView
                                                                         attribute:NSLayoutAttributeTrailing
                                                                        multiplier:1.0
                                                                          constant:10.0]];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel
                                                                         attribute:NSLayoutAttributeTrailing
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:cell.contentView
                                                                         attribute:NSLayoutAttributeTrailing
                                                                        multiplier:1.0
                                                                          constant:-10.0]];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel
                                                                         attribute:NSLayoutAttributeTop
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:cell.contentView
                                                                         attribute:NSLayoutAttributeTop
                                                                        multiplier:1.0
                                                                          constant:3.0]];
    
            [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel
                                                                         attribute:NSLayoutAttributeBottom
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:cell.contentView
                                                                         attribute:NSLayoutAttributeBottom
                                                                        multiplier:1.0
                                                                          constant:-3.0]];
    
        }
        else {
            customImageView = (id)[cell.contentView viewWithTag:IMAGEVIEWTAG];
            customLabel     = (id)[cell.contentView viewWithTag:LABELTAG];
        }
    
        customImageView.image = ...;
        customLabel.text      = ...;
    
        return cell;
    }
    

    Clearly, you'd frequently use a UITableViewCell subclass to facilitate the process of keeping track of your custom controls, but I wanted to keep the example simple.

  2. If you're ever unsure of whether the constraints have been defined unambiguously, run the app and after the UI has been presented, pause the app and enter the following at the (lldb) prompt:

    po [[UIWindow keyWindow] _autolayoutTrace]
    

    This will inform you if any of the views are ambiguously defined (i.e. whether there are any constraints missing).

    If you want to see what the frame is for all of the views, you can enter the following at the (lldb) prompt:

    po [[UIWindow keyWindow] recursiveDescription]
    
  3. Make sure to specify translatesAutoresizingMaskIntoConstraints to NO for all of the subviews, as I did in the above code sample.

  4. While you can define the constraints using constraintWithItem, frequently people will use constraintsWithVisualFormat, as you can often define constraints more concisely that way. Contrast the above code sample with this code sample:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *cellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    
        UIImageView *customImageView;
        UILabel *customLabel;
    
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    
            customImageView = [[UIImageView alloc] init];
            customImageView.translatesAutoresizingMaskIntoConstraints = NO;
            customImageView.tag = IMAGEVIEWTAG;
            [cell.contentView addSubview:customImageView];
    
            customLabel = [[UILabel alloc] init];
            customLabel.translatesAutoresizingMaskIntoConstraints = NO;
            customLabel.tag = LABELTAG;
            [cell.contentView addSubview:customLabel];
    
            NSDictionary *views = NSDictionaryOfVariableBindings(customImageView, customLabel);
            [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-25-[customImageView(30)]-[customLabel]|" options:0 metrics:nil views:views]];
            [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[customImageView]-3-|"                 options:0 metrics:nil views:views]];
            [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[customLabel]-3-|"                     options:0 metrics:nil views:views]];
        }
        else {
            customImageView = (id)[cell.contentView viewWithTag:IMAGEVIEWTAG];
            customLabel     = (id)[cell.contentView viewWithTag:LABELTAG];
        }
    
        customImageView.image = ...;
        customLabel.text      = ...;
    
        return cell;
    }
    
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    From iOS9 you can use the class NSLayoutAnchor to write visual constraints "more concise, and easier to read". – andreacipriani Dec 23 '15 at 14:20
  • @Raiden996 - Yes, that better than the old `addConstraint`/`constraintWithItem` combo, but in this case I'd personally still use VFL (adding eight constraints in three lines of code). – Rob Dec 23 '15 at 17:21
  • for textViews if you want to have your text fit into the textview add these two lines too: [textView sizeToFit]; [keyTextView layoutIfNeeded]; – EhsanR Oct 10 '16 at 20:49