UIStackView 比例布局仅具有内在内容大小

2024-03-12

我在 UIStackView 中排列子视图的布局方面遇到问题,想知道是否有人可以帮助我了解发生了什么。

所以我有 UIStackView 有一些间距(例如 1,但这并不重要)和 .fillProportionally 分布。我添加的排列子视图仅具有 1x1 的内在内容大小(可以是任何东西,只是方形视图),并且我需要它们在 stackView 中按比例拉伸。

问题是,如果我添加没有实际框架而仅具有内在尺寸的视图,那么我会得到错误的布局

否则,如果我添加具有相同大小的框架的视图,一切都会按预期工作,

但我真的不想设置视图的框架。

我很确定这都是关于拥抱和压缩阻力优先级的,但无法弄清楚正确的答案是什么。

这是一个 Playground 示例:

import UIKit
import PlaygroundSupport

class LView: UIView {

    // If comment this and leave only intrinsicContentSize - result is wrong
    convenience init() {
        self.init(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
    }

    // If comment this and leave only convenience init(), then everything works as expected
    public override var intrinsicContentSize: CGSize {
        return CGSize(width: 1, height: 1)
    }
}

let container = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
container.backgroundColor = UIColor.white

let sv = UIStackView()
container.addSubview(sv)


sv.leftAnchor.constraint(equalTo: container.leftAnchor).isActive = true
sv.rightAnchor.constraint(equalTo: container.rightAnchor).isActive = true
sv.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
sv.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
sv.translatesAutoresizingMaskIntoConstraints = false
sv.spacing = 1
sv.distribution = .fillProportionally

// Adding arranged subviews to stackView, 24 elements with intrinsic size 1x1
for i in 0..<24 {
    let a = LView()
    a.backgroundColor = (i%2 == 0 ? UIColor.red : UIColor.blue)
    sv.addArrangedSubview(a)
}
sv.layoutIfNeeded()

PlaygroundPage.current.liveView = container

这显然是实现中的一个错误UIStackView(即系统错误)。

DonMag已经在他的评论中给出了指向正确方向的提示:

当您设置堆栈视图的spacing为 0,一切都按预期进行。但是当您将其设置为任何其他值时,布局就会中断。


这是原因的解释:

ℹ️ 为了简单起见,我假设堆栈视图有

  • a 横轴 and
  • 10 个排列的子视图

随着.fillProportionally分配UIStackView创建系统约束如下:

  • 对于每个排列的子视图,它添加一个等宽约束(UISV-fill-proportionally)与堆栈视图本身相关乘数:

    arrangedSubview[i].width = multiplier[i] * stackView.width
    

    如果你有n在堆栈视图中排列子视图,你会得到n这些限制。让我们称呼他们为proportionalConstraint[i] (where i表示各个视图在视图中的位置arrangedSubviews array).

  • 这些限制是不需要(即它们的优先级不是 1000)。相反,对第一个元素的约束arrangedSubviews第一个数组的优先级为 999,第二个数组的优先级为 998,依此类推:

    proportionalConstraint[0].priority = 999 
    proportionalConstraint[1].priority = 998 
    proportionalConstraint[2].priority = 997 
    proportionalConstraint[3].priority = 996
    ...         
    proportionalConstraint[n–1].priority = 1000 – n
    

    这意味着所需的约束总是会克服这些比例限制!

  • 为了连接排列的子视图(可能有间距),系统还创建n–1称为约束UISV-spacing:

    arrangedSubview[i].trailing + spacing = arrangedSubview[i+1].leading
    

    这些限制是required(即优先级 = 1000)。

  • (系统还将创建一些其他约束(例如,对于垂直轴以及将第一个和最后一个排列的子视图固定到堆栈视图的边缘),但我不会在这里详细介绍,因为它们与理解什么无关出错了。)

苹果的文档 https://developer.apple.com/documentation/uikit/uistackviewdistribution/1616217-fillproportionally on the .fillProportionally分布状态:

一种布局,其中堆栈视图调整其排列视图的大小,以便它们沿着堆栈视图的轴填充可用空间。视图根据其沿堆栈视图轴的内在内容大小按比例调整大小。

所以根据这个multiplier为了proportionalConstraints 应计算如下for spacing = 0:

  • totalIntrinsicWidth = ∑i intrinsicWidth[i]
  • multiplier[i] = intrinsicWidth[i] / totalIntrinsicWidth

如果我们排列的 10 个子视图都具有相同的固有宽度,则这将按预期工作:

multiplier[i] = 0.1

for all proportionalConstraints。然而,一旦我们将间距更改为非零值,则计算multiplier变得更加复杂,因为必须考虑间距的宽度。我已经完成了数学计算和公式multiplier[i] is:


Example:

对于堆栈视图配置如下:

  • 堆栈视图宽度 = 400
  • stackView.spacing = 2

上面的等式将得出:

multiplier[i] = 0.0955

您可以通过将其相加来证明这是正确的:

(10 * width) + (9 * spacing)
    = (10 * multiplier * stackViewWidth) + (9 * spacing)
    = (10 * 0.0955 * 400) + (9 * 2)
    = (0.955 * 400) + 18
    = 382 + 18
    = 400
    = stackViewWidth

然而,系统分配了不同的值:

multiplier[i] = 0.0917431

总宽度加起来为

(10 * width) + (9 * spacing)
    = (10 * 0.0917431 * 400) + (9 * 2)
    = 384,97
    < stackViewWidth

明显地,这个值是错误的。

因此,系统必须打破约束。当然,它打破了最低优先级的约束,即proportionalConstraint最后排列的子视图项目的。

这就是屏​​幕截图中最后排列的子视图被拉伸的原因。

如果您尝试不同的间距和堆栈视图宽度,您最终会得到各种看起来奇怪的布局。但他们都有一个共同点:间距始终优先。(如果将间距设置为更大的值,例如 30 或 40,您将只能看到前两个或三个排列的子视图,因为其余空间已完全被所需间距占据。)


总结一下:

The .fillProportionally分发仅适用于spacing = 0.

对于其他间距,系统会使用不正确的乘数创建约束。

这打破了布局

  • 如果乘数小于应有的值,则必须拉伸任一排列的子视图(最后一个)
  • 如果乘数大于应有的值,则必须压缩多个排列的子视图。

解决这个问题的唯一方法是“滥用”普通UIView具有所需的固定宽度约束作为视图之间的间距。 (通常情况下,UILayoutGuide为此目的引入了 s,但您甚至不能使用它们,因为您无法将布局指南添加到堆栈视图。)

恐怕由于这个错误,没有干净的解决方案可以做到这一点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UIStackView 比例布局仅具有内在内容大小 的相关文章

随机推荐

  • Log4J 1.2 属性配置器 -> Log4J2

    目前 我们的应用程序使用 Log4J 1 2 并使用以下任一方式对其进行配置 File file PropertyConfigurator configure file getAbsolutePath or URL url Property
  • Mailgun:消息“已接受”,但需要很长时间才能送达(或未送达)

    我正在将 Mailgun 用于我维护的网站 通常 Mailgun 工作得很好 但我遇到了一个奇怪的问题 我的脚本调用 HTTP API 使用 Mailgun 发送消息 然后这些消息在我的日志中显示为 已接受 但随后需要很长时间才能 传送 通
  • 获取 HTML 元素中单击位置的文本

    我有一个包含一些文本的 div 元素 当用户单击该 div 内的单词时 我想突出显示该单词 为了做到这一点 我需要知道点击发生在文本中的哪个字符位置 这样我就可以找到附近的空白并在单词周围插入一些格式 找出文本中点击发生的位置是这里的技巧
  • 自动完成后端

    这是一个面试问题 设计一个自动完成的分布式后端 我会回答如下 自动完成是按给定后缀在字典中进行搜索 这本词典可能应该被组织为trie 该词典是根据最常见的查询构建的 但这是另一回事了 现在我假设字典不会经常更改 例如每天一次而不是每毫秒一次
  • 使用断言的最佳实践?

    使用是否存在性能或代码维护问题assert作为标准代码的一部分而不是仅将其用于调试目的 Is assert x gt 0 x is less than zero 比更好或更差 if x lt 0 raise Exception x is l
  • C++ 初始化数组指针

    如何初始化指向文字数组的指针 我希望 grid 指向新分配的 int 数组 1 2 3 int grid new int 3 grid 1 2 3 谢谢 你不能初始化这样就可以动态分配数组 你也不能assign以这种方式到数组 动态或静态
  • 在 OSX 上编译 clisp-2.49:未找到 LIBFFI

    TL DR Even if libffi似乎已安装 configure即使我给它添加 正确的 前缀 脚本也找不到它 这篇文章的最后一部分 是我陷入困境的地方 我仅提供其他信息来解释我如何到达那里 我对这篇长篇文章表示歉意 如果有些内容与您无
  • Git 克隆:“您似乎克隆了一个空存储库”

    我和一些同事一直致力于一个存储在私人 git 存储库中的项目 历史上没有问题 但最近我尝试克隆 遇到了以下问题 Cloning into project warning You appear to have cloned an empty
  • 限制从 Linq 列表中返回的结果数

    我正在使用 Linq EF4 1 从数据库中提取一些结果 并希望将结果限制为 X 个最新结果 其中X是用户设置的数字 有没有办法做到这一点 我目前正在将它们作为List如果这有助于限制结果集 虽然我可以通过循环来限制这一点 直到我点击 X
  • 默认 GNU 链接器脚本名称,以便 VIM 进行语法突出显示

    链接描述文件的常用后缀是什么 以便 VIM 对其使用语法突出显示 好像是 ld 仅据我所知 Vim 没有提供它的语法文件 至少我的没有 7 3 尝试提供下载的内容here http vim 1045645 n5 nabble com Syn
  • 优化内存密集型数据流管道的 GCP 成本

    我们希望降低在 GCP Dataflow 中运行特定 Apache Beam 管道 Python SDK 的成本 我们构建了一个内存密集型 Apache Beam 管道 每个执行器上运行需要大约 8 5 GB RAM 当前正在加载一个大型机
  • C :警告:赋值使指针来自整数而不进行强制转换[默认启用]

    这是我的代码 include
  • Flink TaskManager 超时?

    我正在运行 Flink 应用程序 通过 Yarn 似乎有时任务管理器会随机超时 这是错误 java util concurrent TimeoutException Heartbeat of TaskManager with id some
  • 将日期与 data.table 包一起使用

    我最近发现了 data table 包 现在想知道是否应该替换我的一些 plyr 代码 总而言之 我真的很喜欢plyr 并且我基本上实现了我想要的一切 然而 我的代码运行了一段时间 并且加快速度的前景足以让我运行一些测试 这些测试很快就结束
  • 使用 jQuery 的 Jenkins json REST api 和 CORS 请求

    我正在尝试使用 Jenkins json API 但无法使身份验证正常工作 setup 詹金斯安全 Jenkin s own user database access Matrix gebaseerde beveiliging CORS 通
  • 'Mysql:Class 的未定义方法初始化'

    我的 MySQL 服务器安装一直遇到问题 在断电后变得混乱 配置 运行 OS X 10 6 5 的 Intel i5 Mac已安装红宝石 1 9 2已安装 Rails 3 0 1MySQL 服务器 最终 安装并运行我完全重新安装了MySQL
  • 延迟加载的 React 路由器无论如何都会路由加载

    我一直在尝试使用 React lazy 和 Suspense 在 React 中延迟加载路由 但无论当前路径如何 某些组件都会加载 Feed Profile 和 Settings 请注意 我实际上并不想延迟加载像 MenuAppBar 和
  • TypeError(不可排序类型:int() <= NoneType())

    这是我第一次用 Python 编写代码 需要一些帮助 我正在使用 Python 34 根本无法理解发生了什么 def roll v x input return x v def startGame v 0 while 0 lt v erro
  • 在 Python Sphinx 生成的文档中包含动态内容

    我正在使用 Sphinx 为我的项目生成文档 并在产品安装过程中构建文档 我想在文本和 或代码块中动态包含主机名 我没有在文档中看到任何解释 也没有看到任何包含 shell 命令输出或特定文件中特定行以外的任何内容的工具 有这个功能吗 这里
  • UIStackView 比例布局仅具有内在内容大小

    我在 UIStackView 中排列子视图的布局方面遇到问题 想知道是否有人可以帮助我了解发生了什么 所以我有 UIStackView 有一些间距 例如 1 但这并不重要 和 fillProportionally 分布 我添加的排列子视图仅