Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Container View's Frame Set to CGRectZero when Using Programmatic Auto Layout

I have a UIView subclass that will be a tooltip that can appear on the bottom of any view controller to show a hint/error message/success message/etc. The tooltip view has two subviews: a UILabel on the left-hand side and a UIView on the right hand side, that can be anything (but is often used as a UIButton).

TooltipView.h

@interface TooltipView : UIView

@property (nonatomic, readonly) UILabel *textLabel;

@property (nonatomic) UIView *rightView;

TooltipView.m

static CGFloat const kSubviewToSuperviewSpacing = 7.0f;
static CGFloat const kTextLabelToRightViewHorizontalSpacing = 7.0f;

@implementation TooltipView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];

    if (self) {
        _textLabel = [[UILabel alloc] init];
        _textLabel.numberOfLines = 0;
        [_textLabel setTranslatesAutoresizingMaskIntoConstraints:NO];

        [self addSubview:_textLabel];
    }

    return self;
}

- (void)setRightView:(UIView *)rightView {
    if (_rightView != rightView) {
        _rightView = rightView;

        [self addSubview:_rightView];

        [self setNeedsDisplay];
    }
}

- (BOOL)translatesAutoresizingMaskIntoConstraints {
    return NO;
}

- (void)updateConstraints {
    self.didUpdateConstraints = YES;

    [self addConstraint:[NSLayoutConstraint constraintWithItem:self.textLabel
                                                     attribute:NSLayoutAttributeLeading
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self
                                                     attribute:NSLayoutAttributeLeading
                                                    multiplier:1.0f
                                                      constant:kSubviewToSuperviewSpacing]];

    NSMutableDictionary *metricsDictionary = [@{@"subviewToSuperviewSpacing": @(kSubviewToSuperviewSpacing)} mutableCopy];

    NSMutableDictionary *viewsDictionary = [@{@"textLabel": self.textLabel} mutableCopy];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-subviewToSuperviewSpacing-[textLabel]-subviewToSuperviewSpacing-|"
                                                                 options:0
                                                                 metrics:metricsDictionary
                                                                   views:viewsDictionary]];

    if (self.rightView) {
        metricsDictionary[@"textLabelToRightViewHorizontalSpacing"] = @(kTextLabelToRightViewHorizontalSpacing);
        viewsDictionary[@"rightView"] = self.rightView;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-subviewToSuperviewSpacing-[rightView]-subviewToSuperviewSpacing-|"
                                                                     options:0
                                                                     metrics:metricsDictionary
                                                                       views:viewsDictionary]];

        [self addConstraint:[NSLayoutConstraint constraintWithItem:self.rightView
                                                         attribute:NSLayoutAttributeTrailing
                                                         relatedBy:NSLayoutRelationEqual
                                                            toItem:self
                                                         attribute:NSLayoutAttributeTrailing
                                                        multiplier:1.0f
                                                          constant:kSubviewToSuperviewSpacing]];
    }

    [super updateConstraints];
}

I also have a view controller to test this with a UILabel as the rightView. It subclasses UIViewController, and its only method is overriding loadView:

- (void)loadView {
    TooltipView *tooltipView = [[TooltipView alloc] initWithFrame:CGRectZero];

    tooltipView.textLabel.text = @"Test 1";

    UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    testLabel.text = @"Hello there";
    [testLabel setTranslatesAutoresizingMaskIntoConstraints:NO];

    tooltipView.rightView = testLabel;

    self.view = tooltipView;

}

Finally, in the view controller where I want to display a tooltip, I add the TooltipViewController as a child view controller. Eventually, this will be triggered by some event, but for testing purposes, I have this being added in viewWillAppear:.

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    self.aViewController = [[TooltipViewController alloc] init];
    self.aViewController.view.backgroundColor = [UIColor whiteColor];

    [self addChildViewController:self.aViewController];

    [self.view addSubview:self.aViewController.view];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|"
                                                                  options:0
                                                                  metrics:nil
                                                                    views:@{@"view": self.aViewController.view}]];

    [self.aViewController.view addConstraint:[NSLayoutConstraint constraintWithItem:self.aViewController.view
                                                      attribute:NSLayoutAttributeHeight
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:nil
                                                      attribute:NSLayoutAttributeNotAnAttribute
                                                     multiplier:1.0f
                                                       constant:150.0f]];

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.aViewController.view
                                                      attribute:NSLayoutAttributeBottom
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self.view
                                                      attribute:NSLayoutAttributeBottom
                                                     multiplier:1.0f
                                                       constant:0.0f]];

    [self.aViewController didMoveToParentViewController:self];
}

However, at runtime, the frames of the textLabel and rightView seem to be set correctly, but the frame of the tooltipView is set to CGRectZero, and none of the three views appear on screen.

Thanks in advance!

EDIT

I've even tried removing the textLabel and rightView entirely, so just a plain UIView with leading, trailing, bottom, and height constraints. Nothing is ambiguous, but the frame of the view is still CGRectZero.

like image 454
sethfri Avatar asked Jun 19 '15 02:06

sethfri


1 Answers

It turns out the solution was simply to call setTranslatesAutoresizingMaskIntoConstraints in the initializer of TooltipView rather than overriding the method. This caused the view to appear correctly. h/t to Aaron Thompson (https://twitter.com/aaptho/status/612815498661773312).

like image 51
sethfri Avatar answered Nov 14 '22 21:11

sethfri