Container view frame Set CGRectZero when using automatic program layout

I have a subclass of UIView that will be a tooltip that can be displayed at the bottom of any view controller to display a tooltip / error message / success message / etc. There are two UILabel in the UILabel : a UILabel on the left side and a UIView on the right side, it can be anything (but 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 UILabel as a rightView . It subclasses the UIViewController , and its only method overrides 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 the tooltip, I add TooltipViewController as the child view controller. In the end, this will be triggered by some event, but for testing purposes, I have this addition to 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 textLabel and rightView appear correct, but the tooltipView frame tooltipView set to CGRectZero , and none of the three views appears on the screen.

Thanks in advance!

EDIT

I even tried to completely remove the textLabel and rightView , so it's just a UIView with restrictions on the leading, final, lower and upper levels. Nothing is ambiguous, but the presentation frame is still CGRectZero .

+7
ios objective-c autolayout uiviewcontroller uiview
source share
2 answers

It turns out that the solution was to simply call setTranslatesAutoresizingMaskIntoConstraints in the TooltipView initializer instead of overriding the method. This made the view look right. h / t to Aaron Thompson ( https://twitter.com/aaptho/status/612815498661773312 ).

+6
source share

It looks like you are missing the limitations associated with textLabel and rightView . If you look at the implementation of -updateConstraints , I think the last restrictions can be expressed:

 V:|-kSubviewToSuperviewSpacing-[textLabel]-kSubviewToSuperviewSpacing-| V:|-kSubviewToSuperviewSpacing-[rightView]-kSubviewToSuperviewSpacing-| H:|-kSubviewToSuperviewSpacing-[textLabel] H:[rightView]-kSubviewToSuperviewSpacing-| 

If you add the constraint relation inside the if (self.rightView) like:

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

And then add and else :

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

You will have enough restrictions to uniquely determine the layout. This should trigger the automatic detection of restrictions based on the restrictions when viewing.

If this does not work, you should look at overriding the -intrinsicContentSize method and return intrinsicContentSize textLabel and rightView plus their complement. You will need to call -invalidateIntrinsicContentSize in -setRightView: to make it work correctly.

As a minor point, you do not remove any constraints in -updateConstraints , so when you pass the constraint update that causes this view, you get double constraints. You might want to move some constraint creation to -init and then add / remove the textLabel and rightView to -updateConstraints as needed.

+1
source share

All Articles