使用 SwiftUI 模拟 CSS flex-wrap 行为

Posted

技术标签:

【中文标题】使用 SwiftUI 模拟 CSS flex-wrap 行为【英文标题】:emulating CSS flex-wrap behavior with SwiftUI 【发布时间】:2020-09-14 17:01:41 【问题描述】:

我正在编写一个 SwiftUI 应用程序,并且想要一个容器视图,它可以将其子项“推入”一行,并且一旦该行填满,就开始将后续子项包装到下一行——换句话说,一个模拟容器视图CSS flexbox 的 flex-wrap: wrap 属性的行为。孩子们具有可变的动态宽度,但高度相同(尽管我怀疑大多数非疯狂的解决方案对可变高度具有鲁棒性)。孩子的数量也是动态的。

toplevel,我的问题是:最简单的方法是什么?如果我需要使用 UIKit 或 obj-c 领域,就这样吧,但我更喜欢独立的解决方案(即,不是 pod Yoga)。

(注意:我是 ios 开发新手,但熟悉 React 和所有新奇的 CSS 布局。)

到目前为止,我最好的领先优势是 iOS 14 附带的 LazyVGrid 视图。特别是,在我看来,一个没有最大值(默认为无穷大)的单个自适应 GridItem 应该可以解决问题……但确实如此不是,或者至少我还没弄清楚怎么做。 (我找到的*Grid 的最佳参考斜线唯一详细参考是:https://swiftui-lab.com/impossible-grids/)

我最初尝试过这个:

let columns = [
  GridItem(.adaptive(minimum: 42), alignment: .leading)
]

同时

LazyVGrid(columns: columns, spacing: 10) 
  Text("Hello, world!")
    .fixedSize(horizontal: true, vertical: false)
    .border(Color.gray)
  Text("lol")
    .fixedSize(horizontal: true, vertical: false)
    .border(Color.gray)
  Text("bananananananananana")
    .fixedSize(horizontal: true, vertical: false)
    .border(Color.gray)

but that didn't work。设置显式较大的最大值(50000)也无济于事。我的理解是LazyVGrid 应该允许它的孩子扩展他们的网格单元,但这显然不是这里发生的事情。

我想确保这不是 Text 害羞和温顺并拒绝表达其尺寸偏好,所以我尝试使用框架更明确:

LazyVGrid(columns: columns, spacing: 10) 
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .frame(minWidth: 100, maxWidth: 100)
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)
  Text("sup")
    .border(Color.gray)

but this also doesn't work。我得到了包装行为,但不是“flex”行为,至少根据我的阅读,这是.adaptive 声称在锡上做的事情。

所以我的第二个问题是:我对自适应LazyVGrid 对其子项执行大小建议并协商其“自适应列”的大小的方式有什么误解?有没有办法用网格做我想做的事情,如果没有,为什么不(还有什么更好的计划)?

【问题讨论】:

【参考方案1】:

编辑:意识到我专注于包装而不是您问题的弹性部分,并且不认为这是您所追求的。

我觉得你只需要设置alignment属性就可以得到和CSS flexbox的flex-wrap: wrapshown at CSS tricks.一样的效果。

这似乎对我有用,您的代码的唯一更改是在 LazyVGrid 初始化程序中设置 alignment: .leading。 Result with alignment: .leading

struct ContentView: View 
    
    let columns = [
      GridItem(.adaptive(minimum: 42), alignment: .leading)
    ]
    
    var body: some View 
        LazyVGrid(columns: columns, alignment: .leading, spacing: 10) 
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
          Text("sup")
            .border(Color.gray)
        
    

【讨论】:

【参考方案2】:

我对@9​​87654322@ 的理解不正确——它的列总是均匀分布; maximum 参数只允许这些列扩展(同步),以便它们共同填充父级的空间。我认为每一列都可以单独从最小宽度扩展到最大宽度。

一位朋友纠正了我并将我链接到基于HStack 的解决方案:SwiftUI HStack with Wrap

(我之所以留下这个问题,是因为我找到了最好的Lazy*Grid 文章,上面链接,我认为在这一点上可以双向解释,因此我的错误。)

【讨论】:

以上是关于使用 SwiftUI 模拟 CSS flex-wrap 行为的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI如何模拟视图发光增大的动画效果

SwiftUI如何模拟视图发光增大的动画效果

SwiftUI 中的可模拟 @AppStorage

在 SwiftUI 单元测试中禁用模拟器

SwiftUI 问题(有列表和模拟器)

SwiftUI 核心数据