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

6

我正在对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
@psci 请看一下代码。我想让我的自定义视图有一些信息,而不仅仅是一个蓝色矩形。有一些“最佳”宽度,但如果视图不能从其父视图中获得那么多空间,则可以通过重新排列信息来使用更多的高度进行补偿。intrinsicContentSize没有方法参数说明视图可以占用多少空间。我更新了我的问题以提到这一点。 - Dmitry
这是一个有趣的问题。给问题起个更具描述性的标题可能会有所帮助。正如其他人已经回答过的那样,intrinsicContentSize 是报告 UIView 所需大小的正确方法,但你的问题是如何指定多个有效大小。不幸的是,我不认为使用自动布局可以直接实现这一点。 - Anthony Mattox
@Dmitry 我有一个非常类似的用例,这里的答案并没有真正帮助到我。你最终解决了吗? - manmal
@manmal 不是我想要的方式。我已经在一个视图中留下了 sizeThatFits 函数,这个函数必须由 ViewController 调用(类似于以下内容: override 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() } } - Dmitry
显示剩余2条评论
2个回答

7
您可以使用intrinsicContentSize来报告基于其内容的视图所需的期望大小。这个值随着内容的变化而变化,但是当它发生变化时,您需要调用invalidateIntrinsicContentSizeintrinsicContentSizecontentHuggingPrioritycontentCompressionResistancePriority和父视图中的其他布局约束一起工作,以确定实际的布局大小。
在您的情况下,您想要根据视图内容报告理想大小,如果内容发生更改,则更改大小并使其无效,并使用兄弟视图和父视图的布局约束进行压缩(或阻力)等操作。

2
抱歉,但是你的回答似乎太过笼统。让我举个例子——UILabel可以在横屏模式下将其高度设置为1行,在竖屏模式下或者只有一半宽度时可以设置为2行。我想要实现类似的效果。 由于它是父视图中唯一的子视图,因此对于hugging和resistance并不重要。 我知道如何在ViewControllers级别上使用约束来实现它,但UILabel可以不用约束也能做到。 - Dmitry
如果你知道如何使用约束来完成它,那么我建议你用这种方式。毕竟,这也是UILabel的实现方式。否则你认为它是如何“只获取一半的宽度”的呢? - Scott H
1
问题是UILabel在这种情况下如何请求更多的高度?它成功地报告了它想要的大小,如果封闭的宽度不够,它会请求更多的高度,除非有一些约束阻止它。显然,在自定义视图中也应该有一种方法来实现相同的功能。 - Dmitry
UILabel不会请求更多的高度,它的宽度和高度是由它的约束给定的。它通过根据“行数”、自动缩小等方式以最有效地利用已经给定的空间来呈现文本。同样,您可以使用约束来实现所需的大小。如果需要,您可以在布局过程中的几个点上挂接到调整约束。您还可以选择使用大小类来定义不同的约束集。没有更具体的问题,这就是我能提供的帮助。 - Scott H

2
请注意,在自动布局环境中,即使更改布局,systemLayoutSizeFittingSize也不会被调用。它是一个计算视图完成布局后大小的方法。 内在的内容大小仅涉及视图本身的数据,而不涉及其他视图。请记住,您还可以在任何视图上设置常量宽度或高度约束,并且如果这些维度不随视图内容的变化而变化,则不需要覆盖instrinsicContentSize。
如果使用- (void)updateConstraints方法更新了其约束条件,视图将更新其内容大小,这意味着您可以重写此方法来观察其约束条件的变化。由于您想在某些宽度条件下重新排列视图,因此最好将其放在此- (void)layoutSubviews方法中,该方法将在视图框架发生更改时调用。
- (void)layoutSubviews { 
    [super layoutSubviews];
    // do your view arrangement based on width
    // apple custom constraint here
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接