使用程序化自动布局时容器视图的框架设置为 CGRectZero
Container View's Frame Set to CGRectZero when Using Programmatic Auto Layout
我有一个 UIView
子类,它将是一个工具提示,可以出现在任何视图控制器的底部以显示 hint/error message/success message/etc。工具提示视图有两个子视图:左侧的 UILabel
和右侧的 UIView
,可以是任何东西(但通常用作 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];
}
我还有一个视图控制器来使用 UILabel
作为 rightView
来测试它。它是 UIViewController
的子类,它唯一的方法是覆盖 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;
}
最后,在我想要显示工具提示的视图控制器中,我将 TooltipViewController 添加为子视图控制器。最终,这将由某些事件触发,但出于测试目的,我将其添加到 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];
}
然而,在运行时,textLabel
和rightView
的帧似乎设置正确,但tooltipView
的帧设置为CGRectZero
,并且三个视图中的 none 出现在屏幕上。
提前致谢!
编辑
我什至尝试完全删除 textLabel
和 rightView
,所以只是一个带有前导、尾随、底部和高度限制的普通 UIView。没有任何歧义,但视图的框架仍然是CGRectZero
。
您似乎缺少与 textLabel
和 rightView
相关的约束。查看您的 -updateConstraints
实现,我认为最终约束可以表示为:
V:|-kSubviewToSuperviewSpacing-[textLabel]-kSubviewToSuperviewSpacing-|
V:|-kSubviewToSuperviewSpacing-[rightView]-kSubviewToSuperviewSpacing-|
H:|-kSubviewToSuperviewSpacing-[textLabel]
H:[rightView]-kSubviewToSuperviewSpacing-|
如果在你内部添加约束关系if (self.rightView)
为:
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.textLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.rightView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant: kTextLabelToRightViewHorizontalSpacing]];
然后添加 else
子句:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[textLabel]-subviewToSuperviewSpacing-|"
options:0
metrics:metricsDictionary
views:viewsDictionary]];
您将有足够的约束来明确定义布局。这应该在超级视图中触发基于约束的自动调整大小。
如果这不起作用,您应该考虑覆盖 -intrinsicContentSize
方法和 return textLabel
和 rightView
的 intrinsicContentSize 加上它们的填充。您需要在 -setRightView:
中调用 -invalidateIntrinsicContentSize
才能正常工作。
作为一个小问题,您没有清除 -updateConstraints
中的任何约束,因此在调用此视图的约束更新过程中,您将获得双重约束。您可能希望将一些约束创建移动到 -init
中,然后根据需要仅将 add/remove textLabel
和 rightView
关系移动到 -updateConstraints
中。
事实证明,解决方案只是在 TooltipView
的初始化程序中调用 setTranslatesAutoresizingMaskIntoConstraints
而不是重写该方法。这导致视图正确显示。 h/t 致亚伦汤普森 (https://twitter.com/aaptho/status/612815498661773312)。
我有一个 UIView
子类,它将是一个工具提示,可以出现在任何视图控制器的底部以显示 hint/error message/success message/etc。工具提示视图有两个子视图:左侧的 UILabel
和右侧的 UIView
,可以是任何东西(但通常用作 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];
}
我还有一个视图控制器来使用 UILabel
作为 rightView
来测试它。它是 UIViewController
的子类,它唯一的方法是覆盖 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;
}
最后,在我想要显示工具提示的视图控制器中,我将 TooltipViewController 添加为子视图控制器。最终,这将由某些事件触发,但出于测试目的,我将其添加到 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];
}
然而,在运行时,textLabel
和rightView
的帧似乎设置正确,但tooltipView
的帧设置为CGRectZero
,并且三个视图中的 none 出现在屏幕上。
提前致谢!
编辑
我什至尝试完全删除 textLabel
和 rightView
,所以只是一个带有前导、尾随、底部和高度限制的普通 UIView。没有任何歧义,但视图的框架仍然是CGRectZero
。
您似乎缺少与 textLabel
和 rightView
相关的约束。查看您的 -updateConstraints
实现,我认为最终约束可以表示为:
V:|-kSubviewToSuperviewSpacing-[textLabel]-kSubviewToSuperviewSpacing-|
V:|-kSubviewToSuperviewSpacing-[rightView]-kSubviewToSuperviewSpacing-|
H:|-kSubviewToSuperviewSpacing-[textLabel]
H:[rightView]-kSubviewToSuperviewSpacing-|
如果在你内部添加约束关系if (self.rightView)
为:
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.textLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.rightView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant: kTextLabelToRightViewHorizontalSpacing]];
然后添加 else
子句:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[textLabel]-subviewToSuperviewSpacing-|"
options:0
metrics:metricsDictionary
views:viewsDictionary]];
您将有足够的约束来明确定义布局。这应该在超级视图中触发基于约束的自动调整大小。
如果这不起作用,您应该考虑覆盖 -intrinsicContentSize
方法和 return textLabel
和 rightView
的 intrinsicContentSize 加上它们的填充。您需要在 -setRightView:
中调用 -invalidateIntrinsicContentSize
才能正常工作。
作为一个小问题,您没有清除 -updateConstraints
中的任何约束,因此在调用此视图的约束更新过程中,您将获得双重约束。您可能希望将一些约束创建移动到 -init
中,然后根据需要仅将 add/remove textLabel
和 rightView
关系移动到 -updateConstraints
中。
事实证明,解决方案只是在 TooltipView
的初始化程序中调用 setTranslatesAutoresizingMaskIntoConstraints
而不是重写该方法。这导致视图正确显示。 h/t 致亚伦汤普森 (https://twitter.com/aaptho/status/612815498661773312)。