【问题标题】:How can I get a view's current width and height when using autolayout constraints?使用自动布局约束时如何获取视图的当前宽度和高度?
【发布时间】:2012-11-06 23:15:00
【问题描述】:

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

我尝试遍历其约束以寻找宽度和高度约束,但这不是很干净,并且在存在内在约束时会失败(因为我无法区分两者)。此外,这仅在它们实际上具有宽度和高度约束时才有效,如果它们依赖其他约束来调整大小,则它们不会。

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

【问题讨论】:

  • 我会认为视图的边界在任何特定时间都保持其当前的宽度和高度。这就是在布局过程中调整的内容。
  • 嗯,我从没想过要检查边界。我现在就这样做。

标签: ios autolayout


【解决方案1】:

帧仍然有效。最后,视图使用它的 frame 属性来布置自己。它根据所有约束计算该帧。约束仅用于初始布局(并且任何时候 layoutSubviews 在视图上调用,例如在旋转之后)。之后,位置信息在 frame 属性中。还是你有别的看法?

【讨论】:

  • 我不这么认为。即使直接更改宽度/高度约束(通过更改它们的常量。我在 xib 中有一个 IBOutlet 给它们),框架值也不会改变
  • 我的问题很独特,因为我改变了约束,然后需要在同一个函数中使用框架。调用 setNeedsLayout 不会立即更新它。我想我需要先等待布局然后使用框架。我的第二个问题是,如果我没有直接引用它,我该如何更改高度/宽度约束?
  • 你应该 dispatch_async 然后它允许你延迟你的代码直到下一帧。至于第二部分...我不知道...您必须迭代所有约束并寻找适用的约束...IBOutlets 要容易得多。
  • 适用于 IBOutlets,但我想在一个 UIView 的类别中编写一个函数,我不会使用 IB。
【解决方案2】:

答案是[view layoutIfNeeded]

原因如下:

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

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

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

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

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

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

【讨论】:

  • 一个优秀的答案,也很好解释。在拔掉头发一两个小时后救了我。
  • layoutIfNeeded 很棒,可以立即使用该框架。但是,它还将强制在调用它的视图的子树中的所有视图上呈现约束。例如,如果您以编程方式添加视图,并在递归例程中对所有视图调用 layoutIfNeeded,您可能会发现视图层次结构呈现非常缓慢。 (我学到了这个很难)正如优秀答案中提到的那样,“setNeedsLayout”效率更高,并且会在下一次布局传递时使框架可用,理想情况下发生在大约 1/60 秒内。
  • 我知道这是旧的,但是你在哪里调用这个方法?在initWithCoder?
  • 为什么强制布局只是为了获得边界?这似乎效率低下,并且对于复杂的接口,这可能会很昂贵。而是从 viewDidLayoutSubviews 甚至 viewWillLayoutSubviews 访问最新的边界,让 cocoa 处理自己的布局时间。如果您需要访问值或标志,请设置一个属性以避免在出现问题时多次调用。
  • 是的,如果你需要在运行循环之前访问自动布局计算的值,你只需要强制布局——例如,在当前函数调用的范围内。
【解决方案3】:

我遇到了类似的问题,我需要为 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 方法调用该方法,只有上边框被正确绘制,因为下边框在屏幕外的某个地方。

【讨论】:

  • viewDidLayoutSubviews 被调用了几次,您正在创建大量的图层...您需要在添加 另一个 图层之前添加一些存在性检查。
  • 迟到了几年才发表评论...在覆盖之前,您还应该在超类上调用此方法:[super viewDidLayoutSubviews];
【解决方案4】:

使用-(void)viewWillAppear:(BOOL)animated 并致电[self.view layoutIfNeeded]; - 我试过了。

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

【讨论】:

    【解决方案5】:

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

    只需重写方法:

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

    如果是 UITableViewCell 或 UICollectionViewCell 创建单元格的子类并覆盖相同的方法:

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

    【讨论】:

    • 这是我获得视图真正最终大小的唯一方法
    • 这是我能够获得任何受约束视图的最终大小的唯一方法,因为在我尝试将它们放在 awakeFromNib 上之前,它没有给子视图时间来实际调整大小。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 2019-05-18
    • 2016-04-18
    • 1970-01-01
    • 2014-06-25
    • 1970-01-01
    相关资源
    最近更新 更多