使用自动布局约束时,如何获取视图的当前宽度和高度?


108

我不是在谈论frame属性,因为从那里您只能在xib中获得视图的大小。我说的是由于其约束(可能在旋转后或响应事件)时,何时调整视图的大小。有没有办法获取其当前的宽度和高度?

我尝试遍历其约束以寻找宽度和高度约束,但这并不是很干净,并且在存在固有约束时会失败(因为我无法在两者之间进行区分)。而且,这仅在它们实际具有宽度和高度约束时才有效,而在依靠其他约束来调整大小时则不起作用。

为什么这对我来说如此困难。啊!


我以为该视图的边界在任何特定时间都保持其当前的宽度和高度。这就是在布局过程中进行调整的内容。
菲利普·米尔斯

嗯,我从没想过要检查界限。我现在就做。
yuf 2012年

Answers:


246

答案是[view layoutIfNeeded]

原因如下:

您仍然可以通过检查view.bounds.size.widthview.bounds.size.height(或框架,除非您正在使用,否则是等效的view.transform)来获得视图的当前宽度和高度。

如果您想要的是现有约束所隐含的宽度和高度,则答案不是手动检查约束,因为这将需要您重新实现自动布局系统的整个约束求解逻辑,以便解释这些约束约束。相反,您应该做的只是要求自动布局更新该layout,以便它解决约束并使用正确的解决方案更新view.bounds的值,然后检查view.bounds。

您如何要求自动布局更新布局?[view setNeedsLayout]如果您希望自动布局在运行循环的下一轮更新布局,请致电。

但是,如果您希望它立即更新布局,那么您可以稍后在当前函数中或在运行循环之前的另一点立即访问新的bounds值,那么您需要调用[view setNeedsLayout][view layoutIfNeeded]

您问了第二个问题:“如果我没有直接引用它,如何更改高度/宽度约束?”。

如果在IB中创建约束,最好的解决方案是在视图控制器或视图中创建IBOutlet,这样您就可以直接对其进行引用。如果在代码中创建了约束,则在创建约束时应保留其内部弱属性中的引用。如果其他人创建了约束,那么您需要通过检查检查视图(可能是整个视图层次结构)上的view.constraints属性并执行查找关键NSLayoutConstraint的逻辑来找到它。这可能是错误的方法,因为当不能保证简单地回答该问题时,它还有效地要求您确定哪个特定约束确定了边界大小。最终边界值可以是一个非常复杂的,具有多个约束的系统的解决方案,


16
一个很好的答案,也很好地解释了。一两个小时把我的头发拔出来,救了我。
Ben Kreeger

2
layoutIfNeeded很棒,它将使框架立即可用。但是,这还将强制在对其进行调用的视图的子树中的所有视图上呈现约束。例如,如果要以编程方式添加视图,并layoutIfNeeded在递归例程中调用所有视图,则可能会发现视图层次结构的渲染速度非常慢。(我很辛苦地学到了这一点。)正如在出色答案中提到的那样,“ setNeedsLayout”效率更高,并且可以在下一次布局遍历中使帧可用,理想情况下,此过程发生在大约1/60秒内。
shmim

1
我知道这很旧,但是您在哪里调用此方法?在initWithCoder
MayNotBe 2014年

4
为什么要强制布局只是为了达到界限?这似乎效率低下,而且接口复杂,这可能会很昂贵。而是从viewDidLayoutSubviews甚至viewWillLayoutSubviews访问最新边界,并让可可处理自己的布局时间。如果需要访问值或标志,则可以设置一个属性,以避免出现多次调用。
smileBot 2015年

1
是的,仅在需要在运行循环之前访问自动布局计算值的情况下才需要进行布局,例如在当前函数调用范围内。
藻类

8

我遇到了类似的问题,我需要UITableView根据的约束设置在上添加上下边框,以调整其大小UIStoryboard。我可以使用访问更新的约束- (void)viewDidLayoutSubviews。这很有用,因此您无需继承视图的子类并覆盖其布局方法。

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

在不从viewDidLayoutSubview方法调用方法的情况下,仅正确绘制了顶部边框,因为底部边框位于屏幕外。


3
viewDidLayoutSubviews被调用了几次,您正在创建大量的图层...您需要在添加另一个图层之前添加一些存在性检查。
Kalzem '16

迟到几年后再评论...在重写之前,您还应该在超类上调用此方法:[super viewDidLayoutSubviews];
AlejandroIván16年

5

对于那些仍可能面临此类问题的人,尤其是TableviewCell。

只需重写方法:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

对于UITableViewCell或UICollectionViewCell,请创建单元的子类并重写相同的方法:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

这是我可以获得视图最终真实尺寸的唯一途径
Benoit Jadinon

这是我能够获得任何受约束视图的最终大小的唯一方法,因为在我尝试将它们放在awakeFromNib上之前,这没有给子视图实际首先调整大小的时间。谢谢!
Azin Mehrnoosh

3

使用-(void)viewWillAppear:(BOOL)animated和致电 [self.view layoutIfNeeded];-我尝试过的作品。

因为如果您使用-(void)viewDidLayoutSubviews它肯定会起作用,但是每次您的UI需要更新/更改时都会调用此方法。这将变得难以管理。软键是您使用布尔变量来避免此类调用循环。更好的使用viewWillAppearviewWillAppear如果再次加载视图(不重新分配),还将记住该调用。


2

框架仍然有效。最后,视图使用其frame属性进行布局。它基于所有约束条件计算该框架。约束仅用于初始布局(和在旋转后在视图上调用layoutSubviews的任何时间)。之后,位置信息在frame属性中。还是您看到其他内容?


1
我看到其他情况。即使直接更改了宽度/高度约束(通过更改其常数,框架值也不会更改。在xib中我有一个IBOutlet)
yuf 2012年

我的问题很独特,因为我更改了约束,然后需要在同一函数中使用框架。调用setNeedsLayout不会立即更新。我想我需要先等待布局,然后再使用框架。我的第二个问题是,如果我没有直接引用它,如何更改高度/宽度约束?
yuf 2012年

1
您应该然后使用dispatch_async,它将允许您将代码延迟到下一帧。至于第二部分...我不知道...您将不得不迭代所有约束并寻找适用的约束... IBOutlets更加容易。
borrrden

对于IBOutlets是正确的,但是我想为UIView的类别编写一个函数,而我不会使用IB。
yuf 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.