我不是在谈论 frame 属性,因为从中您只能在 xib 中获取视图的大小。我说的是视图何时因约束而调整大小(可能在旋转之后,或响应事件)。有没有办法得到它当前的宽度和高度?
我尝试遍历其约束以寻找宽度和高度约束,但这不是很干净,并且在存在内在约束时会失败(因为我无法区分两者)。此外,这仅在它们实际上具有宽度和高度约束时才有效,如果它们依赖于其他约束来调整大小,则它们不会。
为什么这对我来说如此困难。啊!
答案是[view layoutIfNeeded]
。
原因如下:
您仍然可以通过检查 view.bounds.size.width
和 view.bounds.size.height
(或框架,除非您使用 view.transform
,否则它是等效的)来获得视图的当前宽度和高度。
如果您想要的是现有约束所隐含的宽度和高度,那么答案不是手动检查约束,因为这需要您重新实现自动布局系统的整个约束解决逻辑才能解释那些约束。相反,您应该做的只是要求自动布局更新该布局,以便它解决约束并使用正确的解决方案更新 view.bounds 的值,然后检查 view.bounds。
你如何要求自动布局更新布局?如果您希望自动布局在运行循环的下一轮更新布局,请调用 [view setNeedsLayout]
。
但是,如果您希望它立即更新布局,以便您可以稍后在当前函数中或在运行循环之前的另一点立即访问新的边界值,那么您需要调用 [view setNeedsLayout]
和 [view layoutIfNeeded]
。
您问了第二个问题:“如果我没有直接引用它,我该如何更改高度/宽度约束?”。
如果您在 IB 中创建约束,最好的解决方案是在您的视图控制器或视图中创建一个 IBOutlet,以便您可以直接引用它。如果您在代码中创建了约束,那么您应该在创建它时保留内部弱属性中的引用。如果其他人创建了约束,那么您需要通过检查视图上的 view.constraints 属性,可能还有整个视图层次结构,并实现找到关键 NSLayoutConstraint 的逻辑来找到它。这可能是错误的方法,因为它还有效地要求您确定哪个特定约束决定了边界大小,而不能保证对该问题有一个简单的答案。最终边界值可能是对具有多个约束、具有多个优先级等的非常复杂的系统的解决方案,因此没有单个约束是最终值的“原因”。
我有一个类似的问题,我需要为 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
方法调用该方法,则只会正确绘制上边框,因为下边框位于屏幕外的某个位置。
[super viewDidLayoutSubviews];
对于那些可能仍然面临此类问题的人,尤其是 TableviewCell。
只需覆盖该方法:
-(void)layoutSubviews
{
//your code here like drawing a shadow
}
如果是 UITableViewCell 或 UICollectionViewCell 创建单元格的子类并覆盖相同的方法:
-(void)layoutSubviews
{
//your code here like drawing a shadow
}
使用 -(void)viewWillAppear:(BOOL)animated
并调用 [self.view layoutIfNeeded];
- 我试过了。
因为如果您使用 -(void)viewDidLayoutSubviews
,它肯定会起作用,但是每次您的 UI 需要更新/更改时都会调用此方法。这将很难管理。软键是您使用布尔变量来避免这种调用循环。更好地使用 viewWillAppear
。请记住,如果再次加载视图(不重新分配),也会调用 viewWillAppear
。
框架仍然有效。最后,视图使用它的 frame 属性来布置自己。它根据所有约束计算该帧。约束仅用于初始布局(并且任何时候 layoutSubviews 在视图上调用,例如在旋转之后)。之后,位置信息在 frame 属性中。或者你有其他看法?
layoutIfNeeded
很棒,并且会立即使框架可用。但是,它还将强制在调用它的视图的子树中的所有视图上呈现约束。例如,如果您以编程方式添加视图,并在递归例程中对所有视图调用layoutIfNeeded
,您可能会发现视图层次结构呈现非常缓慢。 (我学到了这个艰难的方式)正如出色的答案中提到的,“setNeedsLayout”效率更高,并且会在下一次布局传递时使框架可用,理想情况下发生在大约 1/60 秒内。initWithCoder
?