如何在 SwiftUI 中创建网格
How to create grid in SwiftUI
我知道我们可以像这样在垂直 SwiftUI 中创建一个列表,
struct ContentView : View {
var body: some View {
NavigationView {
List {
Text("Hello")
}
}
}
}
但是有什么方法可以像我们在 UICollectionView
中那样将列表分成 2 个或 3 个或更多个像网格一样覆盖屏幕的跨度
您可以像这样创建自定义视图以实现 UICollectionView 行为:-
struct ContentView : View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
ScrollView(showsHorizontalIndicator: true) {
HStack {
ForEach(0...10) {_ in
GridView()
}
}
}
List {
ForEach(0...5) {_ in
ListView()
}
}
Spacer()
}
}
}
struct ListView : View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello World!"/*@END_MENU_TOKEN@*/)
.color(.red)
}
}
struct GridView : View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Image("marker")
.renderingMode(.original)
.cornerRadius(5)
.frame(height: 200)
.border(Color.red)
Text("test")
}
}
}
签出基于 ZStack 的示例 here
Grid(0...100) { _ in
Rectangle()
.foregroundColor(.blue)
}
iOS/iPadOS 14 Xcode 12 可用。您可以使用 LazyVGrid 只加载用户在屏幕上看到的内容而不是整个列表,List 默认情况下是惰性的。
import SwiftUI
//MARK: - Adaptive
struct ContentView: View {
var body: some View {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum:100))]) {
ForEach(yourObjects) { object in
YourObjectView(item: object)
}
}
}
}
}
//MARK: - Custom Columns
struct ContentView: View {
var body: some View {
ScrollView {
LazyVGrid(columns: Array(repeating: GridItem(), count: 4)) {
ForEach(yourObjects) { object in
YourObjectView(item: object)
}
}
}
}
}
不要忘记将信息 objects 替换为您的信息,并将 YourObjectView 替换为您的 customView.
iOS14
您可以使用 2 个新的原生 View
:
LazyHGrid
LazyVGrid
使用代码或直接来自库:
该库包含一个完整的示例代码,您可以自行测试。
SwiftUI 的 LazyVGrid 和 LazyHGrid 为我们提供了相当灵活的网格布局。
最简单的网格由三部分组成:您的原始数据、描述您想要的布局的 GridItem 数组,以及汇集在一起的 LazyVGrid 或 LazyHGrid您的数据和布局。
例如,这将使用大小为 80 磅的单元格创建垂直网格布局:
struct ContentView: View {
let data = (1...100).map { "Item \([=10=])" }
let columns = [
GridItem(.adaptive(minimum: 80))
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
使用 GridItem(.adaptive(minimum: 80)) 意味着我们希望网格在每行中容纳尽可能多的项目,每个项目的最小尺寸为 80 点.
如果你想控制列数,你可以使用 .flexible() 代替,这也让你指定每个项目应该有多大,但现在让你控制如何有很多专栏。例如,这将创建五列:
struct ContentView: View {
let data = (1...100).map { "Item \([=11=])" }
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
第三种选择是使用固定尺寸。例如,这将使第一列恰好为 100 点宽,并允许第二列填满所有剩余的 space:
struct ContentView: View {
let data = (1...100).map { "Item \([=12=])" }
let columns = [
GridItem(.fixed(100)),
GridItem(.flexible()),
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
您还可以使用 LazyHGrid 制作水平滚动网格,其工作方式大致相同,只是它在其初始化程序中接受行。
例如,我们可以创建 10 个水平滚动的并排标题图像,如下所示:
struct ContentView: View {
let items = 1...50
let rows = [
GridItem(.fixed(50)),
GridItem(.fixed(50))
]
var body: some View {
ScrollView(.horizontal) {
LazyHGrid(rows: rows, alignment: .center) {
ForEach(items, id: \.self) { item in
Image(systemName: "\(item).circle.fill")
.font(.largeTitle)
}
}
.frame(height: 150)
}
}
}
如您所见,创建水平和垂直网格所需的代码几乎相同,只是更改列的行。
如果您需要支持 iOS13,您将无法访问 LazyHGrid 或 LazyVGrid,因此请阅读下面的替代方法……
SwiftUI 为我们提供了用于垂直布局的 VStack 和用于水平布局的 HStack,但两者都没有——没有任何东西可以在网格结构中布置视图。
幸运的是,我们可以利用 SwiftUI 的视图构建器系统自己编写一个。这意味着编写一个必须使用行数和列数创建的类型,加上一个闭包,它可以 运行 检索网格中给定单元格的视图。在 body 中,它可以遍历所有行和列,并在 VStack 和 HStack 中创建单元格以制作网格,每次调用视图闭包询问单元格中应该包含什么。
在代码中它看起来像这样:
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
var body: some View {
VStack {
ForEach(0 ..< rows, id: \.self) { row in
HStack {
ForEach(0 ..< columns, id: \.self) { column in
content(row, column)
}
}
}
}
}
init(rows: Int, columns: Int, @ViewBuilder content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
}
// An example view putting GridStack into practice.
struct ContentView: View {
var body: some View {
GridStack(rows: 4, columns: 4) { row, col in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
}
}
这将创建一个 4x4 网格,每个单元格中都有一个图像和文本。
快乐编码;)
我知道我们可以像这样在垂直 SwiftUI 中创建一个列表,
struct ContentView : View {
var body: some View {
NavigationView {
List {
Text("Hello")
}
}
}
}
但是有什么方法可以像我们在 UICollectionView
您可以像这样创建自定义视图以实现 UICollectionView 行为:-
struct ContentView : View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
ScrollView(showsHorizontalIndicator: true) {
HStack {
ForEach(0...10) {_ in
GridView()
}
}
}
List {
ForEach(0...5) {_ in
ListView()
}
}
Spacer()
}
}
}
struct ListView : View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello World!"/*@END_MENU_TOKEN@*/)
.color(.red)
}
}
struct GridView : View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Image("marker")
.renderingMode(.original)
.cornerRadius(5)
.frame(height: 200)
.border(Color.red)
Text("test")
}
}
}
签出基于 ZStack 的示例 here
Grid(0...100) { _ in
Rectangle()
.foregroundColor(.blue)
}
iOS/iPadOS 14 Xcode 12 可用。您可以使用 LazyVGrid 只加载用户在屏幕上看到的内容而不是整个列表,List 默认情况下是惰性的。
import SwiftUI
//MARK: - Adaptive
struct ContentView: View {
var body: some View {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum:100))]) {
ForEach(yourObjects) { object in
YourObjectView(item: object)
}
}
}
}
}
//MARK: - Custom Columns
struct ContentView: View {
var body: some View {
ScrollView {
LazyVGrid(columns: Array(repeating: GridItem(), count: 4)) {
ForEach(yourObjects) { object in
YourObjectView(item: object)
}
}
}
}
}
不要忘记将信息 objects 替换为您的信息,并将 YourObjectView 替换为您的 customView.
iOS14
您可以使用 2 个新的原生 View
:
LazyHGrid
LazyVGrid
使用代码或直接来自库:
该库包含一个完整的示例代码,您可以自行测试。
SwiftUI 的 LazyVGrid 和 LazyHGrid 为我们提供了相当灵活的网格布局。
最简单的网格由三部分组成:您的原始数据、描述您想要的布局的 GridItem 数组,以及汇集在一起的 LazyVGrid 或 LazyHGrid您的数据和布局。
例如,这将使用大小为 80 磅的单元格创建垂直网格布局:
struct ContentView: View {
let data = (1...100).map { "Item \([=10=])" }
let columns = [
GridItem(.adaptive(minimum: 80))
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
使用 GridItem(.adaptive(minimum: 80)) 意味着我们希望网格在每行中容纳尽可能多的项目,每个项目的最小尺寸为 80 点.
如果你想控制列数,你可以使用 .flexible() 代替,这也让你指定每个项目应该有多大,但现在让你控制如何有很多专栏。例如,这将创建五列:
struct ContentView: View {
let data = (1...100).map { "Item \([=11=])" }
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
第三种选择是使用固定尺寸。例如,这将使第一列恰好为 100 点宽,并允许第二列填满所有剩余的 space:
struct ContentView: View {
let data = (1...100).map { "Item \([=12=])" }
let columns = [
GridItem(.fixed(100)),
GridItem(.flexible()),
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
您还可以使用 LazyHGrid 制作水平滚动网格,其工作方式大致相同,只是它在其初始化程序中接受行。
例如,我们可以创建 10 个水平滚动的并排标题图像,如下所示:
struct ContentView: View {
let items = 1...50
let rows = [
GridItem(.fixed(50)),
GridItem(.fixed(50))
]
var body: some View {
ScrollView(.horizontal) {
LazyHGrid(rows: rows, alignment: .center) {
ForEach(items, id: \.self) { item in
Image(systemName: "\(item).circle.fill")
.font(.largeTitle)
}
}
.frame(height: 150)
}
}
}
如您所见,创建水平和垂直网格所需的代码几乎相同,只是更改列的行。
如果您需要支持 iOS13,您将无法访问 LazyHGrid 或 LazyVGrid,因此请阅读下面的替代方法……
SwiftUI 为我们提供了用于垂直布局的 VStack 和用于水平布局的 HStack,但两者都没有——没有任何东西可以在网格结构中布置视图。
幸运的是,我们可以利用 SwiftUI 的视图构建器系统自己编写一个。这意味着编写一个必须使用行数和列数创建的类型,加上一个闭包,它可以 运行 检索网格中给定单元格的视图。在 body 中,它可以遍历所有行和列,并在 VStack 和 HStack 中创建单元格以制作网格,每次调用视图闭包询问单元格中应该包含什么。
在代码中它看起来像这样:
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
var body: some View {
VStack {
ForEach(0 ..< rows, id: \.self) { row in
HStack {
ForEach(0 ..< columns, id: \.self) { column in
content(row, column)
}
}
}
}
}
init(rows: Int, columns: Int, @ViewBuilder content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
}
// An example view putting GridStack into practice.
struct ContentView: View {
var body: some View {
GridStack(rows: 4, columns: 4) { row, col in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
}
}
这将创建一个 4x4 网格,每个单元格中都有一个图像和文本。
快乐编码;)