NSLayoutConstraint 子视图不会自动调整大小以适应容器视图
Posted
技术标签:
【中文标题】NSLayoutConstraint 子视图不会自动调整大小以适应容器视图【英文标题】:NSLayoutConstraint subviews won't autoresize to fit container view 【发布时间】:2013-07-31 17:31:11 【问题描述】:我有一个简单的布局,其中有一个在整个屏幕上开始的containerView
。在里面,我有两个垂直的子视图——topView (green), botView (orange)
,它们需要相等的高度,并且应该有 containerView
高度的 50%。
启动后,我从屏幕外从底部向上滑动视图(蓝色),导致containerView
调整到较短的高度。
我期望发生的是topView
和botView
都会变短,在containerView
内保持它们的均匀高度,但实际发生的是内部视图没有调整大小,尽管有限制这样做。
因为这段代码有一些基于代码的约束,还有一些 IB 约束,here 是一个示例项目,可以看到它的实际运行情况。这是被忽略的约束:
NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:self.topView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeHeight
multiplier:0.5f
constant:0.f];
[self.containerView addConstraint:c1];
和调整大小的视图代码:
- (void)resizeViews
__weak UAViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^
[UIView animateWithDuration:0.25 animations:^
weakSelf.slideInView.frame = CGRectMake(CGRectGetMinX(weakSelf.view.bounds),
CGRectGetMaxY(weakSelf.view.bounds) - CGRectGetHeight(weakSelf.slideInView.frame),
CGRectGetWidth(weakSelf.view.bounds),
CGRectGetHeight(weakSelf.slideInView.frame));
weakSelf.containerView.frame = CGRectMake(CGRectGetMinX(weakSelf.view.bounds),
CGRectGetMinY(weakSelf.view.bounds),
CGRectGetWidth(weakSelf.view.bounds),
CGRectGetMaxY(weakSelf.view.bounds) - CGRectGetHeight(weakSelf.slideInView.frame));
];
);
我在这里做错了什么?
【问题讨论】:
对了,你为什么要在resizeViews
中做dispatch_async
?这没有必要(除非您从其他队列中调用它,如果您这样做我会感到惊讶)。但这没有实际意义,因为如果您正在执行自动布局,则根本不应该使用 frame
属性。修改您的约束,并让约束引擎做出适当的frame
更改。
dispatch_async 正在为此调试应用程序的计时器线程调用。
【参考方案1】:
我很惊讶您将frame
设置为视图。通常在自动布局中,您可以通过更改约束 constant
而不是通过更改 frame
在视图中滑动。
例如,假设您对容器子视图的约束是这样定义的(为简单起见,由于两个视图应该占据容器的 50%,最容易通过将两个子视图定义为相同来完成高度):
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topView][botView(==topView)]|" options:0 metrics:nil views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[topView]|" options:0 metrics:nil views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[botView]|" options:0 metrics:nil views:views]];
并且,为了简单起见,定义容器之间的关系,视图中的幻灯片与其父视图的关系如下:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[containerView][slideInView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[containerView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[slideInView]|" options:0 metrics:nil views:views]];
// set the slide in view's initial height to zero
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:slideInView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:0.0];
[self.view addConstraint:heightConstraint];
然后,当您想在“视图中滑动”中滑动时,只需对其高度的变化进行相应的动画处理即可:
heightConstraint.constant = 100;
[UIView animateWithDuration:1.0 animations:^
[self.view layoutIfNeeded];
];
因此:
现在,您也可以使用容器高度的 x% 来执行此操作,但您无法使用 VFL 轻松执行此操作,但它应该可以正常工作。您也可以通过为其顶部约束设置动画来滑入“视图中的幻灯片”(但这很麻烦,因为您必须重新计算旋转的顶部约束,这是我不想在这个简单的模型中做的事情)。
但你明白了。只需将容器视图的高度或底部约束定义为与视图中的幻灯片相关,然后通过调整其约束为视图中的幻灯片设置动画,如果您已正确定义所有约束,它将优雅地为所有在更改幻灯片中视图的约束后为layoutIfNeeded
设置动画时,四个视图(容器、视图中的幻灯片和容器的两个子视图)正确。
【讨论】:
您说的很有道理,我已将更改应用到我的项目,但我无法复制您的屏幕截图 - “无法同时满足约束”。你能把你在这里使用的修改过的项目传回来,这样我就可以知道我哪里出错了? @coneybeare 通常无法满足约束是由于您忽略了将translatesAutoresizingMaskIntoConstraints
设置为NO
以编程方式创建的视图。我必须承认,我没有看过你的项目(有时通过别人的项目可能会很麻烦),而且从头开始这样做更容易。我刚刚创建了一个空白应用程序,并将以下代码放入视图控制器的viewDidLoad
:pastie.org/8194565# 希望这能说明这个概念。
我能够使用粘贴代码复制您的结果,但我很难理解为什么在使用 IB 生成的视图时同样的事情不起作用。我在所有 5 个视图上将 translatesAutoresizingMaskIntoConstraints 设置为 NO,情节提要中没有任何约束,但继续出现错误。你能看看这个(非常简单的)示例项目吗? cloud.coneybeare.net/QZ6G
我的理论是从 IB 实例化的视图自动应用了系统定义的帧大小约束,但我没有看到阻止自动创建这些的选项。
@coneybeare 不,我不认为是这样。但是 Xcode 4.6 中的 Interface Builder 可能会令人愤怒,因为它不断尝试添加约束以消除歧义。 Xcode 5 在这方面要好得多。但我发现我通常能够在 Xcode 4.6 中与 IB 搏斗,才能做到这一点。今晚我会看看你的项目。以上是关于NSLayoutConstraint 子视图不会自动调整大小以适应容器视图的主要内容,如果未能解决你的问题,请参考以下文章
NSLayoutConstraint - 无法将子视图框架设置为父视图边界
调整 UITableViewCell 大小的 NSLayoutConstraint 错误
NSLayoutConstraint 用于代码中未知数量的子视图