根据 SwiftUI 中的宽度计算每行的项目数
Calculate number of items per row based on their width in SwiftUI
这是我之前问题的扩展 ()
我需要实现一个布局,其中每行的项目数根据它们的组合宽度动态确定(基本上,将项目排成一行直到它们不再适合)。
有人告诉我,使用 GeometryReader 是用声明性语言做某事的一种 hacky 方式,这显然是正确的。
我也被引导到这个类似 CollectionView 的组件 https://github.com/Q-Mobile/QGrid 但解决方案是静态的,因为行数和每行的单元格数在呈现任何组件之前确定一次。
我不知道如何处理这个问题,所以任何建议对我来说都非常有价值!
❤️❤️❤️
这是一个使用 GeometryReader
的两步过程。
- 测量每个项目的宽度,共
content(item)
。
- 使用尺寸将项目排列成行。
唯一的问题是它必须在每次重绘时重新计算或缓存先前测量的宽度,但这不一定是少数项目的问题。
我不会 post 此处的代码,因为它使用了 GeometryReader
,这不是作者想要使用的东西。
TL;DR
GeometryReader 可能是 "hacky" 解决方案,但它是我们目前拥有的解决方案。 可以创建一个动态回流少量项目或延迟回流大量项目的解决方案。 My demo code 在这里会很笨拙,但听起来描述我的方法可能会有用。
利用现有资源
在幕后,SwiftUI 正在执行各种优化的约束求解以有效地布局您的视图。从理论上讲,像您描述的那样重排内容可能是解决约束的一部分;在今天的 SwiftUI 中,它不是。因此,执行您所描述的操作的唯一方法是以下的一些变体:
- 让 SwiftUI 根据我们的数据模型布置所有内容。
- 获取 SwiftUI 使用几何 reader 和 preferences/callbacks 决定的宽度。
- 使用这些宽度来解决我们的回流约束。
- 更新数据模型,这将触发第 1 步。
希望这个过程收敛到一个稳定的布局,而不是进入死循环。
我的成绩
在试用它之后,这就是我到目前为止得到的结果。您可以看到,当宽度更改时,少量项目(在我的示例中为 29 个)几乎立即回流。对于大量项目(在我的示例中为 262),会有明显的延迟。如果内容和视图宽度不变并且不需要经常更新,这应该不是什么大问题。时间几乎全部花在了步骤 1 上,所以在我们在 SwiftUI 中获得适当的回流支持之前,我怀疑这已经很好了。 (如果你想知道,一旦回流完成,垂直滚动视图就会以正常的响应速度滚动。)
我的攻略
本质上,我的数据模型从一个 [String]
数组开始,然后 t运行 将其形成一个 [[String]]
数组,其中每个内部数组对应一条水平适合的行我的看法。 (从技术上讲,它以 String
开始,在白色 space 上拆分以形成 [String]
,但从广义上讲,我有一个集合,我想拆分成多行。 ) 然后我可以使用 VStack
、HStack
和 ForEach
.
进行布局
我的第一个方法是尝试读取我正在显示的实际视图的宽度。但是,我很快 运行 陷入无限递归或异常不稳定的振荡,因为它可能会截断文本视图(例如 [Four] [score] [and] [se...]),然后一旦回流改变,来回(或只是以截断状态结束。
所以我决定作弊。我在第二个看不见的水平滚动视图中布置了所有单词。这样,它们都可以根据需要占用尽可能多的 space,永远不会被截断,而且最重要的是,因为此布局仅依赖于 [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]]
。
总结
现在,这在运输应用程序中是否有用将取决于您要回流的项目数量,以及它们在布局后是静态的还是经常更改。但我发现这是一个有趣的消遣!
这是我之前问题的扩展 (
我需要实现一个布局,其中每行的项目数根据它们的组合宽度动态确定(基本上,将项目排成一行直到它们不再适合)。
有人告诉我,使用 GeometryReader 是用声明性语言做某事的一种 hacky 方式,这显然是正确的。
我也被引导到这个类似 CollectionView 的组件 https://github.com/Q-Mobile/QGrid 但解决方案是静态的,因为行数和每行的单元格数在呈现任何组件之前确定一次。
我不知道如何处理这个问题,所以任何建议对我来说都非常有价值!
❤️❤️❤️
这是一个使用 GeometryReader
的两步过程。
- 测量每个项目的宽度,共
content(item)
。 - 使用尺寸将项目排列成行。
唯一的问题是它必须在每次重绘时重新计算或缓存先前测量的宽度,但这不一定是少数项目的问题。
我不会 post 此处的代码,因为它使用了 GeometryReader
,这不是作者想要使用的东西。
TL;DR
GeometryReader 可能是 "hacky" 解决方案,但它是我们目前拥有的解决方案。 可以创建一个动态回流少量项目或延迟回流大量项目的解决方案。 My demo code 在这里会很笨拙,但听起来描述我的方法可能会有用。
利用现有资源
在幕后,SwiftUI 正在执行各种优化的约束求解以有效地布局您的视图。从理论上讲,像您描述的那样重排内容可能是解决约束的一部分;在今天的 SwiftUI 中,它不是。因此,执行您所描述的操作的唯一方法是以下的一些变体:
- 让 SwiftUI 根据我们的数据模型布置所有内容。
- 获取 SwiftUI 使用几何 reader 和 preferences/callbacks 决定的宽度。
- 使用这些宽度来解决我们的回流约束。
- 更新数据模型,这将触发第 1 步。
希望这个过程收敛到一个稳定的布局,而不是进入死循环。
我的成绩
在试用它之后,这就是我到目前为止得到的结果。您可以看到,当宽度更改时,少量项目(在我的示例中为 29 个)几乎立即回流。对于大量项目(在我的示例中为 262),会有明显的延迟。如果内容和视图宽度不变并且不需要经常更新,这应该不是什么大问题。时间几乎全部花在了步骤 1 上,所以在我们在 SwiftUI 中获得适当的回流支持之前,我怀疑这已经很好了。 (如果你想知道,一旦回流完成,垂直滚动视图就会以正常的响应速度滚动。)
我的攻略
本质上,我的数据模型从一个 [String]
数组开始,然后 t运行 将其形成一个 [[String]]
数组,其中每个内部数组对应一条水平适合的行我的看法。 (从技术上讲,它以 String
开始,在白色 space 上拆分以形成 [String]
,但从广义上讲,我有一个集合,我想拆分成多行。 ) 然后我可以使用 VStack
、HStack
和 ForEach
.
我的第一个方法是尝试读取我正在显示的实际视图的宽度。但是,我很快 运行 陷入无限递归或异常不稳定的振荡,因为它可能会截断文本视图(例如 [Four] [score] [and] [se...]),然后一旦回流改变,来回(或只是以截断状态结束。
所以我决定作弊。我在第二个看不见的水平滚动视图中布置了所有单词。这样,它们都可以根据需要占用尽可能多的 space,永远不会被截断,而且最重要的是,因为此布局仅依赖于 [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]]
。
总结
现在,这在运输应用程序中是否有用将取决于您要回流的项目数量,以及它们在布局后是静态的还是经常更改。但我发现这是一个有趣的消遣!