报告 UIView 子类所需大小的正确方法是啥?

Posted

技术标签:

【中文标题】报告 UIView 子类所需大小的正确方法是啥?【英文标题】:What is a correct way to report desired size for UIView subclass?报告 UIView 子类所需大小的正确方法是什么? 【发布时间】:2015-09-30 09:57:50 【问题描述】:

我正在继承 UIView 并且找不到正确的方法来请求/报告其所需的大小。我希望systemLayoutSizeFittingSize 能做到这一点,但它从未被调用过。布局很简单 - 我固定视图的顶部和前导,并将尾随和底部限制为小于或等于***视图。在所有调整大小的函数中,只有intrinsicContentSize 被调用,但这个函数不是很有帮助。

import Foundation
import UIKit

class StatsView: UIView

  override func drawRect(rect: CGRect)
  
    let context=UIGraphicsGetCurrentContext()!
    CGContextSetRGBFillColor(context, 0, 0, 1, 1)
    CGContextFillRect(context, rect)
  

  override func sizeThatFits(size: CGSize) -> CGSize
  
    print("Called sizeThatFits with \(size)")
    if(size.width>350)
    
      return CGSize(width: 350,height: 50)
    
    else
    
      return CGSize(width: 50,height: 100)
    
  

  override func systemLayoutSizeFittingSize(size: CGSize) -> CGSize
  
    print("Called systemLayoutSizeFittingSize with \(size)")
    return super.systemLayoutSizeFittingSize(size)
  

  override func systemLayoutSizeFittingSize(size: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize
  
    print("Called systemLayoutSizeFittingSize with \(size)")
    if(size.width>350)
    
      return CGSize(width: 350,height: 50)
    
    else
    
      return CGSize(width: 50,height: 100)
    
  

  override func intrinsicContentSize() -> CGSize
  
    super.intrinsicContentSize()
    print("Called intrinsicContentSize")
    return CGSize(width: 10,height: 50)
  

什么是正确的做法?

更新我希望我的视图有一些信息,而不仅仅是一个蓝色矩形。有一些“最佳”宽度,但如果视图不能从其父视图获得那么多,它可以通过重新排列信息以使用更多高度来补偿。因此,从intrinsicContentSize 报告常量值不符合我的需求。

【问题讨论】:

为什么你说intrinsicContentSize 没用?这是您自定义视图可以向布局系统报告其所需大小的确切位置。 @psci 请看代码。我希望我的自定义视图有一些信息,而不仅仅是一个蓝色矩形。有一些“最佳”宽度,但如果视图不能从它的父级获得那么多,它可以通过重新排列信息以使用更多高度来补偿。 intrinsicContentSize 没有方法的参数来说明视图可以占用多少空间。我更新了我的问题以提及它。 这是一个有趣的问题。有一个更具描述性的标题可能会有所帮助。正如已经回答的那样,intrinsicContentSize 是报告UIView 所需大小的正确方法,但您的问题是如何指定 多个 有效大小。不幸的是,我认为开箱即用的自动布局是不可能的。 @Dmitry 我有一个非常相似的用例,这里的答案并没有真正的帮助。你有没有想过? @manmal 不是我想要的方式。我在视图中留下了sizeThatFits 函数,该函数必须由 ViewController 调用(类似这样:覆盖 func viewDidLayoutSubviews() updateHeight() fileprivate func updateHeight() stats.layoutIfNeeded() let WantedSize=stats. sizeThatFits(stats.bounds.size) if WantedSize.height != statsHeightConstraint.constant statsHeightConstraint.constant=wantedSize.height view.layoutIfNeeded() 【参考方案1】:

您使用intrinsicContentSize 根据其内容报告所需的视图大小。这个值可以随着内容的变化而变化,但需要调用invalidateIntrinsicContentSize

intrinsicContentSizecontentHuggingPrioritycontentCompressionResistancePriority 以及父视图中的其他布局约束一起确定实际布局大小。

在您的情况下,听起来您想根据视图的内容报告理想的大小,更改大小并在内容更改时使其无效,并使用具有兄弟视图和父视图的布局约束来压缩(或抵抗)根据需要。

【讨论】:

抱歉,您的回答似乎太笼统了。让我举个例子 - UILabel 可以将它的高度安排为横向模式下的 1 行和纵向模式下的 2 行,或者如果它只有宽度的一半。我想达到类似的效果。这里没有内容变化,并且作为父视图中唯一的子视图,拥抱和抵抗并不重要。我知道如何借助 ViewControllers 级别的约束来做到这一点,但 UILabel 没有它也可以。 如果你知道如何在约束的帮助下做到这一点,那么我建议你这样做。毕竟,UILabel 也是这样做的。您还怎么认为它“只有一半宽度”? 问题是 UILabel 在这种情况下如何要求更高的高度?它成功地报告了它想要的大小,如果封闭宽度不够,它会请求更高的高度,除非某些限制阻止它。显然应该有一种方法可以在自定义视图中做同样的事情。 UILabel 不要求更高的高度,它的宽度和高度是由它的约束条件决定的。它通过以不同的方式呈现文本(根据lines、自动缩小等)以最有效地使用已分配给它的空间来对此做出响应。同样,您应该能够使用约束来实现所需的大小。如有必要,您可以在几个点与布局过程挂钩以调整约束。您还可以选择使用大小类来定义不同的约束集。如果没有更具体的问题,我能提供的就这么多。【参考方案2】:

请注意,在自动布局环境中systemLayoutSizeFittingSize 不会调用,即使它改变布局。这是一种在视图完成布局后计算视图大小的方法。

intrinsic 内容大小仅与视图本身中的数据有关,与其他视图中的数据无关。请记住,您还可以在任何视图上设置恒定的宽度或高度约束,如果这些尺寸不会随着视图内容的变化而变化,则不需要覆盖 instrinsicContentSize

如果使用- (void)updateConstraints 方法发生的约束发生任何更新,视图将更新其内容大小,这意味着您可以重写此方法以观察其约束的变化。由于您想在特定宽度条件下重新排列视图,因此最佳做法是使用此 - (void)layoutSubviews 方法,该方法将调用视图框架的更改。

- (void)layoutSubviews  
    [super layoutSubviews];
    // do your view arrangement based on width
    // apple custom constraint here

【讨论】:

我实际上没有子视图,只有一些自定义绘图,但这可能是个好主意。我会试一试,看看它是否有效。 我实际上在你的答案中添加了一些代码,但后来被删除了:-(问题是如果视图不符合限制,layoutSubviews 会被调用,否则不会被调用。所以,当我切换到更大的宽度时,我无法调整大小以使用它:-( 但我比上面的答案更喜欢这个答案。 调整视图大小时?请您用图表/图片/屏幕截图更新您的问题,这将有助于我们清楚地了解您的问题。请注意,如果您使用这些方法 '- (void)setNeedsLayout; ,layoutSubviews 也会触发; - (void)layoutIfNeeded;'允许您在绘图周期发生之前执行布局。 -layoutIfNeeded 尽早强制布局 我想避免不必要的细节。基本上它是一个包含许多细节和许多复杂绘图的统计视图。但是,如果它有足够的宽度将它想要的所有东西放入 1 行中,它确实看起来会更好。如果没有,它可以用更小的宽度和更高的高度来做。它也将嵌入到滚动视图中,但是,我想用最简单的情况来解决它。所以,我简化了绘图功能,并把滚动视图排除在我的问题之外。

以上是关于报告 UIView 子类所需大小的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

子类化 UIView 是处理未知数量的这些自定义对象的正确方法吗?

NSScrollView 内容大小不正确

为 UIView 子类加载 Nib 的正确方法

iPhone UIView - 调整框架以适应子视图

目前设置 UIView 角半径的“正确”方法是啥?

创建后调整uiview子类的大小