更新于 2020 年 7 月 - 最新的 SwiftUI 现在有内置组件来完成此操作,应优先使用它们。

FlowStack

FlowStack 是一个 SwiftUI 组件,用于在网格中布局内容。

要求

在 MacOS 10.14 或 10.15 上的 Xcode 11 beta

安装

在 Xcode 中,选择 File -> Swift Packages -> Add Package Dependency 并输入 此仓库的 URL

用法

FlowStack(columns, numItems, alignment) { index, colWidth in }

columns (Int)
要显示的列数。
numItems (Int)
您将要显示的项目总数。
alignment (HorizontalAlignment?)
默认值:.leading
最后一行的任何尾随列的对齐方式。

回调参数

index (Int)
当前正在处理的项目的索引。
colWidth (CGFloat)
当前正在处理的列的计算宽度。

示例

1) 简单示例

最简单的例子

FlowStack(columns: 3, numItems: 27, alignment: .leading) { index, colWidth in
  Text(" \(index) ").frame(width: colWidth)
}

您应该始终将 .frame(width: colWidth) 添加到 FlowStack 的直接子元素。

Screen Shot 2019-06-25 at 10 43 41 PM

2) 显示数据

struct Item {
  var image: String
  var label: String
}

let items = [
  Item(image: "hand.thumbsup", label: "Up"),
  Item(image: "tortoise", label: "Tortoise"),
  Item(image: "forward", label: "Forward"),
  Item(image: "hand.thumbsdown", label: "Down"),
  Item(image: "hare", label: "Hare"),
  Item(image: "backward", label: "Backward")
]

FlowStack(columns: 3, numItems: items.count, alignment: .leading) { index, colWidth in
  Button(action: { print("Tap \(index)!") }) {
    Image(systemName: items[index].image)
    Text(items[index].label).font(Font.caption)
  }
  .padding()
  .frame(width: colWidth)
}

Screen Shot 2019-06-25 at 10 54 25 PM

内边距/边框/操作

让我们在单元格上绘制一个边框,以可视化一些概念

FlowStack(columns: 3, numItems: 27, alignment: .leading) { index, colWidth in
  Text(" \(index) ").frame(width: colWidth).border(Color.gray)
}

Screen Shot 2019-06-25 at 11 03 05 PM

现在让我们交换 .frame.border 的顺序,并注意发生了什么。 这表明**在链接布局修饰符时,操作顺序很重要**。

FlowStack(columns: 3, numItems: 27, alignment: .leading) { index, colWidth in
  Text(" \(index) ").border(Color.gray).frame(width: colWidth)
}

Screen Shot 2019-06-25 at 11 04 58 PM

现在让我们把顺序换回来,并添加一些内边距

FlowStack(columns: 3, numItems: 27, alignment: .leading) { index, colWidth in
  Text(" \(index) ").padding().frame(width: colWidth).border(Color.gray)
}

Screen Shot 2019-06-25 at 11 10 10 PM

要添加操作,您当然可以**像示例 #2 一样,直接在您的单元格中放置按钮**。 但还有一种方法可以检测到对整个单元格的点击。 请注意,我们添加了一个背景来检测文本外部空白区域的点击。

FlowStack(columns: 3, numItems: 27, alignment: .leading) { index, colWidth in
  Text(" \(index) ")
    .padding()
    .frame(width: colWidth)
    .border(Color.gray)
    .background(Color.white)
    .tapAction {
      print("Tap!")
    }
}

带有图像的示例

这是一个带有图像的例子。 LoadableImageView 来自 这里

FlowStack(columns: 3, numItems: 27, alignment: .leading) { index, colWidth in
  VStack {
    LoadableImageView(with: "https://cataas.com/cat?type=sq?foo")
      .padding()
      .frame(width: colWidth, height: colWidth)
      .tapAction { print("Meow!") }
    Text(" \(index) ")
  }
  .padding()
  .frame(width: colWidth)
  .border(Color.gray)
  .background(Color.white)
  .tapAction {
    print("Tap!")
  }
}

Screen Shot 2019-06-25 at 11 41 18 PM

均匀间隔的网格

FlowStack(columns: 4, numItems: 27, alignment: .leading) { index, colWidth in
  LoadableImageView(with: "https://cataas.com/cat?type=sq?rando")
    .padding(5)
    .frame(width: colWidth, height: colWidth)
}.padding(5)

Screen Shot 2019-06-26 at 12 13 21 AM

反馈

如果您遇到问题或发现错误,请提交 github issue。