0

I am trying to achieve the following layout programmatically:

enter image description here

It is a subclassed UIView, on which I would like to place a UILabel with fix height (40 pixels), dynamic width (width is calculated based on the length of the text, so I guess this could be considered fix, not dynamic because I only calculate it one time), and exactly 40 pixels from the left and bottom.

Here is my code:

- (UIView *) initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {

    marginGeneral       = 40.0f;

    UILabel *titleLabel = [[UILabel alloc] init];
    [titleLabel setTranslatesAutoresizingMaskIntoConstraints: NO];
    titleLabel.backgroundColor  = [UIColor blackColor];
    titleLabel.textColor        = [UIColor whiteColor];
    titleLabel.text             = @"Some Text";
    [self addSubview:titleLabel];

    NSLayoutConstraint *contsTitleLeft = [NSLayoutConstraint
                                          constraintWithItem: titleLabel
                                          attribute: NSLayoutAttributeLeft
                                          relatedBy: NSLayoutRelationEqual
                                          toItem: self
                                          attribute: NSLayoutAttributeLeft
                                          multiplier: 1.0
                                          constant: marginGeneral];

    NSLayoutConstraint *contsTitleRight = [NSLayoutConstraint
                                           constraintWithItem: titleLabel
                                           attribute: NSLayoutAttributeRight
                                           relatedBy: NSLayoutRelationEqual
                                           toItem: self
                                           attribute: NSLayoutAttributeRight
                                           multiplier: 1.0
                                           constant: marginGeneral * -1.0f];

    NSLayoutConstraint *contsTitleBottom = [NSLayoutConstraint
                                            constraintWithItem: titleLabel
                                            attribute: NSLayoutAttributeBottom
                                            relatedBy: NSLayoutRelationEqual
                                            toItem: self
                                            attribute: NSLayoutAttributeBottom
                                            multiplier: 1.0
                                            constant: marginGeneral * -1.0f];

    NSLayoutConstraint *contsTitleHeight = [NSLayoutConstraint
                                            constraintWithItem: titleLabel
                                            attribute: NSLayoutAttributeHeight
                                            relatedBy: NSLayoutRelationEqual
                                            toItem: nil
                                            attribute: NSLayoutAttributeNotAnAttribute
                                            multiplier: 1.0
                                            constant: 40.0f];

    [self addConstraints:@[contsTitleLeft, contsTitleRight, contsTitleBottom]];
    [titleLabel addConstraint:contsTitleHeight];

    }

return self;
}

@end

This of course gives back all kinds of constraint-related warnings and the label does not show up.

Could you nice people help me understand where did I go wrong and how to correct it? Thank you very much!

P.s.: I do not use interface builder, so that is not an option. :)

3
  • What error does it throw? Commented Nov 6, 2018 at 8:49
  • It is a wall of text, but here is a short example: [LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x2820a48c0 UILabel:0x104f1dec0'NGC 1499: The California ...'.left == UIViewAPOD:0x104f13430.left + 20 (inactive)> When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug. Commented Nov 6, 2018 at 9:03
  • You need add subview before adding constraints. Commented Nov 6, 2018 at 9:08

1 Answer 1

1

First of all, you never added you titleLabel as subview.

Second, you need to ignore autoresizing masks to avoid conflicts in constraints.

Third, you are adding height constraint to view, whereas it belongs to label.

Here is the code that works:

UILabel *titleLabel = [[UILabel alloc] init];
[titleLabel setTranslatesAutoresizingMaskIntoConstraints: NO];
titleLabel.backgroundColor  = [UIColor blackColor];
titleLabel.textColor        = [UIColor whiteColor];
titleLabel.text             = @"Some Text";
[self.view addSubview: titleLabel];

NSLayoutConstraint *contsTitleLeft = [NSLayoutConstraint
                                      constraintWithItem: titleLabel
                                      attribute: NSLayoutAttributeLeft
                                      relatedBy: NSLayoutRelationEqual
                                      toItem: self
                                      attribute: NSLayoutAttributeLeft
                                      multiplier: 1.0
                                      constant: marginGeneral];

NSLayoutConstraint *contsTitleRight = [NSLayoutConstraint
                                       constraintWithItem: titleLabel
                                       attribute: NSLayoutAttributeRight
                                       relatedBy: NSLayoutRelationEqual
                                       toItem: self
                                       attribute: NSLayoutAttributeRight
                                       multiplier: 1.0
                                       constant: marginGeneral * -1.0f];

NSLayoutConstraint *contsTitleBottom = [NSLayoutConstraint
                                        constraintWithItem: titleLabel
                                        attribute: NSLayoutAttributeBottom
                                        relatedBy: NSLayoutRelationEqual
                                        toItem: self
                                        attribute: NSLayoutAttributeBottom
                                        multiplier: 1.0
                                        constant: marginGeneral * -1.0f];

NSLayoutConstraint *contsTitleHeight = [NSLayoutConstraint
                                        constraintWithItem: titleLabel
                                        attribute: NSLayoutAttributeHeight
                                        relatedBy: NSLayoutRelationEqual
                                        toItem: nil
                                        attribute: NSLayoutAttributeNotAnAttribute
                                        multiplier: 1.0
                                        constant: 40.0f];

[self addConstraints:@[contsTitleLeft, contsTitleRight, contsTitleBottom]];
[titleLabel addConstraint: contsTitleHeight];

UPD: Adding custom view to view controller in viewDidLoad:

CustomView *customView = [[CustomView alloc] initWithFrame: CGRectMake(0, 0, 200, 200)];
[customView setTranslatesAutoresizingMaskIntoConstraints: NO];
[customView setBackgroundColor: [UIColor greenColor]];
[self.view addSubview: customView];

NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem: customView
                                                                       attribute: NSLayoutAttributeWidth
                                                                       relatedBy: NSLayoutRelationEqual
                                                                          toItem: nil
                                                                       attribute: NSLayoutAttributeNotAnAttribute
                                                                      multiplier:1
                                                                        constant:200];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem: customView
                                                                       attribute: NSLayoutAttributeHeight
                                                                       relatedBy: NSLayoutRelationEqual
                                                                          toItem: nil
                                                                       attribute: NSLayoutAttributeNotAnAttribute
                                                                      multiplier:1
                                                                        constant:200];

[customView addConstraints: @[widthConstraint, heightConstraint]];

NSLayoutConstraint *centerHorizontallyConstraint = [NSLayoutConstraint constraintWithItem: customView
                                                                                    attribute: NSLayoutAttributeCenterX
                                                                                    relatedBy: NSLayoutRelationEqual
                                                                                       toItem: self.view
                                                                                    attribute: NSLayoutAttributeCenterX
                                                                                   multiplier: 1
                                                                                     constant: 0];

NSLayoutConstraint *centerVerticallyConstraint = [NSLayoutConstraint constraintWithItem: customView
                                                                                    attribute: NSLayoutAttributeCenterY
                                                                                    relatedBy: NSLayoutRelationEqual
                                                                                       toItem: self.view
                                                                                    attribute: NSLayoutAttributeCenterY
                                                                                   multiplier: 1
                                                                                     constant: 0];

[self.view addConstraints: @[centerHorizontallyConstraint, centerVerticallyConstraint]];
Sign up to request clarification or add additional context in comments.

5 Comments

Label was added as a subview, I just forgot to paste it here. :) Also, adding the height to the label or the superview works both (I tried just now). I assume the ignoring of the autoresizing masks was the real error. Thank you, it works now! However, it still gives me loads of warnings: "[LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x280f13840 UILabel:0x1055121a0'NGC 1499: The California ...'.right == UIViewAPOD:0x10541b7a0.right - 20 (inactive)>"
Hi, it did make it work. But I updated my reply above 'coz it still gives me loads of warnings: [LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x280f134d0 UILabel:0x1055121a0'NGC 1499: The California ...'.left == UIViewAPOD:0x10541b7a0.left + 20 (inactive)> When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
Then the problem might be somewhere else in your custom view. How do you add it to superview? What constraints do you use? Did you set translating autoresizing masks into constraints to NO? Because, I am adding the label to controller's root view and it gives me no warnings.
This is unbelievable. Now, I get no warnings, but the label is not showing up at all. I get a glimpse of it when I rotate the device at the bottom, but that is all. I have updated the original question with the current code.
I've updated my answer to show how I add the custom view to view controller. Please, compare. I don't have any warnings and I can see the view and its label before and after rotation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.