在 SwiftUI 中,List 是构建列表式界面非常重要的组件。它可以增,删,移动列表数据,下面我们一起来看看。
下面是初始效果
struct ListSample: View {
@State var fruits: [String] = [
"🍎", "🍊", "🍇", "🍓"
]
var body: some View {
NavigationView {
List {
Section {
ForEach(fruits, id: .self) { fruit in
HStack {
Text(fruit)
.frame(width: 60, height: 60)
.font(.title)
.background(
Circle()
.fill(Color.green.opacity(0.3))
)
Text("(Int.random(in: 1..<10)) kg")
.font(.subheadline)
}
}
.onDelete { indexSet in
fruits.remove(atOffsets: indexSet)
}
.onMove { indexSet, index in
fruits.move(fromOffsets: indexSet, toOffset: index)
}
.listRowBackground(Color.green.opacity(0.1))
} header: {
HStack {
Text("🥑")
Text("fruit".capitalized)
.foregroundColor(Color.black)
Text("((fruits.count))")
.font(.subheadline)
.fontWeight(.semibold)
}
}
}
.navigationTitle(Text("Grocery list"))
.navigationBarTitleDisplayMode(.automatic)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Add") {
fruits.append("🥝")
}
}
}
}
}
}
上述示例,是一个完整的增,删,移动的代码。
增加
由于我们给数据源的修饰符是带有 @State, 所以当我们往数组添加数据,列表就会自动刷新。因为当有数据更新,被 @State 修饰的属性会自动通知被使用的组件进行数据重新刷新。
所以当我们点击右上角的添加按钮时,我们往数组里面添加了一条数据。所以列表就会自动刷新
Button("Add") {
fruits.append("🥝")
}
删除
我们目前的删除逻辑是耦合在UI一起的,其实我们应该分开UI和业务逻辑。目前的删除代码如下:
.onDelete { indexSet in
fruits.remove(atOffsets: indexSet)
}
我们给List增加了onDelete方法,当我们左滑就可以看到删除按钮,当我们点击删除按钮就会删除掉当前这一行。
当我们点击onDelete方法查看苹果提供的官方文档时,我们会看到此删除方法需要一个IndexSet 参数。
我们可以改造一下现有代码。我们先把逻辑和UI分开. 代码如下:
.onDelete { indexSet in
delete(indexSet: indexSet)
}
func delete(indexSet: IndexSet) {
fruits.remove(atOffsets: indexSet)
}
我们把删除逻辑提取成一个函数,这样UI部分就只专注UI,不需要嵌套逻辑了。
但是苹果给我们提供了很简便的方式来书写代码,其实我们只需要传入删除方法的名称就可以完成删除功能了,简便写法方法如下:
.onDelete(perform: delete) // delete 是方法名称
// 下面方法不变
func delete(indexSet: IndexSet) {
fruits.remove(atOffsets: indexSet)
}
移动
移动也很简单,也只需要一行代码。但是我们目前的业务逻辑依然和UI耦合在一起的。还是需要和删除方法一样。把UI部分和逻辑分开
UI和逻辑一起的代码:
.onMove { indexSet, index in
fruits.move(fromOffsets: indexSet, toOffset: index)
}
分开后的代码:
.onMove(perform: move)
func move(indexSet: IndexSet, index: Int) {
fruits.move(fromOffsets: indexSet, toOffset: index)
}
其实添加的逻辑代码和UI也是耦合的,也是需要分开的。
listStyle
这是一个很重要的属性,它可以改变List的外观。下面我们一起来看看。为了更好的看出效果。我增加了一些数据。最终效果如下
struct ListSample: View {
@State var fruits: [String] = [
"🍎", "🍊", "🍇", "🍓"
]
var vegetables: [String] = [
"🥦", "🥒", "🧅", "🥬"
]
var body: some View {
NavigationView {
List {
Section {
ForEach(fruits, id: .self) { fruit in
HStack {
Text(fruit)
.frame(width: 50, height: 50)
.font(.title)
.background(
Circle()
.fill(Color.green.opacity(0.3))
)
Text("(Int.random(in: 1..<10)) kg")
.font(.subheadline)
}
}
.onDelete(perform: delete)
.onMove(perform: move)
.listRowBackground(Color.green.opacity(0.1))
} header: {
HStack {
Text("🥑")
Text("fruit".capitalized)
.foregroundColor(Color.black)
Text("((fruits.count))")
.font(.subheadline)
.fontWeight(.semibold)
}
} footer: {
Text("🦶 fruit")
}
Section {
ForEach(vegetables, id: .self) { vegetable in
HStack {
Circle()
.fill(Color.yellow.opacity(0.3))
.frame(width: 50, height: 50)
.overlay(
Text(vegetable)
.font(.title)
)
Text("(Int.random(in: 1..<10)) kg")
.font(.subheadline)
}
}
.listRowBackground(Color.yellow.opacity(0.1))
} header: {
HStack {
Text("🥕")
Text("vegetable".capitalized)
.foregroundColor(Color.black)
Text("((vegetables.count))")
.font(.subheadline)
.fontWeight(.semibold)
}
} footer: {
Text("🦶 vegetable")
}
}
.navigationTitle(Text("Grocery list"))
.navigationBarTitleDisplayMode(.automatic)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Add") {
fruits.append("🥝")
}
}
}
}
}
func delete(indexSet: IndexSet) {
fruits.remove(atOffsets: indexSet)
}
func move(indexSet: IndexSet, index: Int) {
fruits.move(fromOffsets: indexSet, toOffset: index)
}
}
需要注意的时,其实Section内部的代码应该提取出来,避免一个方法内部过长。但是这个任务就交给你吧。你来试试?
ListStyle总共有六个值
- automatic
- sidebar
- insetGrouped
- grouped
- inset
- plain
下面来做一个效果对比,以方便你在日后的开发中。更能一目了然的知道效果。
以上就是今天要说的内容,希望你喜欢。
大家有什么看法呢?欢迎留言讨论。
公众号:RobotPBQ