【问题标题】:Calculate number of items per row based on their width in SwiftUI根据 SwiftUI 中的宽度计算每行的项目数
【发布时间】:2019-12-26 22:22:34
【问题描述】:

这是我上一个问题的延伸 (Get width of a view using in SwiftUI)

我需要实现一个布局,其中每行的项目数是根据它们的组合宽度动态确定的(基本上,将项目放在一行中,直到它们不再适合为止)。

有人告诉我,使用 GeometryReader 是一种用声明性语言做某事的 hacky 方式,这显然是正确的。

我也被定向到这个类似于 CollectionView 的组件 https://github.com/Q-Mobile/QGrid,但解决方案是静态的,因为在呈现任何组件之前,每行的行数和单元格数被确定一次。

我不知道如何解决这个问题,所以任何建议对我来说都很有价值!

❤️❤️❤️

【问题讨论】:

    标签: ios swift xcode layout swiftui


    【解决方案1】:

    这是一个使用GeometryReader 的两步过程。

    1. 为每个项目测量content(item)宽度
    2. 使用度量将项目排列成行。

    唯一的问题是它必须在每次重绘时重新计算或缓存之前测量的宽度,但这不一定是少数项目的问题。

    我不会在这里发布代码,因为它使用了GeometryReader,这不是作者想要使用的。

    【讨论】:

    • 如果我理解正确,GeometryReader 会读取其父级的大小。但是这里的父级(在这种情况下是集合视图)需要获取其子级的大小。如何在视图树中向上传播元素的大小,以便集合视图可以使用它来构造行?
    • @DamiaanDufaux 您可以为孩子添加.background(GeometryReader) 以获得它的大小,您可以随意传输它。 (属性、回调、发布者等)
    【解决方案2】:

    TL;DR

    GeometryReader 可能是一个“hacky”解决方案,但它是我们目前拥有的解决方案。 可以创建一个动态重排少量项目或延迟重排大量项目的解决方案。 My demo code 在这里会很笨拙,但听起来描述我的方法可能很有用。

    利用现有资源工作

    在幕后,SwiftUI 正在执行各种优化的约束求解,以有效地布局您的视图。从理论上讲,像您描述的那样重排内容可能是该约束解决的一部分;在今天的 SwiftUI 中,它不是。因此,执行您所描述的操作的唯一方法是以下一些变体:

    1. 让 SwiftUI 根据我们的数据模型布置一切。
    2. 获取 SwiftUI 决定使用几何阅读器和首选项/回调的宽度。
    3. 使用这些宽度来解决我们的回流限制。
    4. 更新数据模型,这将触发步骤 1。

    希望这个过程收敛到一个稳定的布局,而不是进入一个无限循环。

    我的结果

    在玩弄它之后,这就是我到目前为止所得到的。您可以看到,随着宽度的更改,少量项目(在我的示例中为 29 个)几乎瞬间回流。对于大量项目(在我的示例中为 262 个),存在明显的延迟。如果内容和视图宽度没有改变并且不需要经常更新,这应该不是什么大问题。时间几乎都花在了第 1 步中,所以在我们在 SwiftUI 中获得适当的回流支持之前,我怀疑这已经是最好的了。 (如果您想知道,一旦回流完成,垂直滚动视图会以正常的响应速度滚动。)

    我的策略

    基本上,我的数据模型从[String] 数组开始,然后将其转换为[[String]] 数组,其中每个内部数组对应于一条水平线,可以在我的视图中水平放置。 (从技术上讲,它以String 开头,在空格上拆分形成[String],但从广义上讲,我有一个要拆分为多行的集合。)然后我可以使用@ 对其进行布局987654329@、HStackForEach

    我的第一种方法是尝试从我正在显示的实际视图中读取宽度。但是,我很快遇到了无限递归或奇怪的不稳定振荡​​,因为它可能会截断文本视图(例如 [Four] [score] [and] [se...]),然后在回流改变后取消截断一次,返回来回(或只是以截断状态结束。

    所以我决定作弊。我在第二个不可见的水平滚动视图中布置了所有单词。这样,它们都可以占用尽可能多的空间,永远不会被截断,最重要的是,因为这个布局只依赖于 [String] 数组而不是派生的 [[String]] 数组,它永远不能进入递归环形。您可能认为将每个视图放置两次(一次用于测量宽度,一次用于显示)效率低下,但我发现它比尝试从显示的视图测量宽度快几十倍,并且 100% 产生正确的结果时间。

    +---------- FIRST TRY - CYCLIC ----------+  +-------- SECOND TRY - ACYCLIC --------+
    |                                        |  |                                      |
    |    +--------+ [String] +----------+    |  |   +-------+ [String] +--------+      |
    |    |                              |    |  |   |                           |      |
    |    | +--------------------------+ |    |  |   v                           v      |
    |    | |                          | |    |  | Hidden +-->  Widths  +--> [[String]] |
    |    v v                          + v    |  | layout                        |      |
    |  Display +-->  Widths  +--> [[String]] |  |                               v      |
    |  layout                                |  |                            Display   |
    |                                        |  |                            layout    |
    +----------------------------------------+  +--------------------------------------+
    

    为了读取和保存宽度,我采用了 GeometryReader/PreferenceKey 方法detailed on swiftui-lab.com。宽度保存在视图模型中,并在隐藏的滚动视图中的视图数量或大小发生变化时更新。此类更改(或更改视图的宽度)然后根据模型中保存的宽度将 [String] 数组重排为 [[String]]

    总结

    现在,这在运输应用程序中是否有用将取决于您要重排的项目数量,以及它们在布局后是静态的还是经常更改。但我发现这是一种迷人的消遣!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-18
      • 2013-08-26
      • 1970-01-01
      • 2011-08-30
      • 1970-01-01
      • 2015-09-29
      相关资源
      最近更新 更多