【问题标题】: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() } }

标签: ios swift uiview


【解决方案1】:

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

此intrinsicContentSize 与contentHuggingPriority、contentCompressionResistancePriority 以及父视图中的其他布局约束一起确定实际布局大小。

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

【讨论】:

  • 抱歉,您的回答似乎太笼统了。让我举个例子 - 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 行中,它确实看起来会更好。如果没有,它可以用更小的宽度和更高的高度来做。它也将嵌入到滚动视图中,但是,我想用最简单的情况来解决它。所以,我简化了绘图功能,并把滚动视图排除在我的问题之外。
猜你喜欢
  • 1970-01-01
  • 2016-11-19
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 2017-12-09
  • 2013-10-04
  • 1970-01-01
  • 2013-03-02
相关资源
最近更新 更多