SwiftUI
中的List
可是个好东西,它用于显示可滚动列表的视图容器,类似于UITableView
。在List
中可以显示静态或动态的数据,并支持垂直滚动。List
是一个数据驱动的视图,当数据发生变化时,列表会自动更新。
针对List
,我们还可以进行添加、移动、删除以及滑动等功能。
显示静态数据
显示静态的数据还是比较简单的,比如要显示12个月份:
上面代码中,直接在List
组件里面放置Text
组件即可,不过如果同类型数据过多,还是采用动态数组的方式去显示。
显示动态数据
很多的时候采用的都是动态数组显示的方式,比如通过网络请求回来的数据,再通过List
显示出来等等。
上面的代码简化了很多,我们将months
数据作为第一个参数,传给了List
,而List
的第二个参数id
,则表示months
数据中的元素采用自身作为唯一的标识符。如果数组中的两个或多个元素不是唯一的,这可能会导致问题。
如果不用字符串数据,而是该用了实例对象数组,则可以将实例对象的结构实现Identifiable
协议,该协议需要实现一个id
属性,如果将一个实现了Identifiable
协议的实例对象数组传给List,则不需要指定id
参数了。比如下面的代码示例:
样式设置
默认的List
是有背景色,Light
模式下是白色,Dark
模式下是灰色,比如下面这个代码:
背景色
只需要添加一行代码即可,将其修饰在List
闭包内部的组件上,即修饰在Text
组件上。如果直接修饰在List
组件上,则没有任何效果。
.listRowBackground(Color.blue)
内间距
另外上面的代码中,Text
的宽度没有完全和cell的宽度对齐,两边还有一些间距,有的时候是不需要这个间距的,设置下面代码即可,同样是要修饰在List
闭包内的组件上。
.listRowInsets(EdgeInsets())
分割线颜色与隐藏
设置分割线以及是否显示需要将下面两个修饰符作用在List
闭包内的组件上。
// 隐藏分割线,默认是显示的。
.listRowSeparator(.hidden)
// 设置分割线颜色。
.listRowSeparatorTint(.red)
设置Cell之间的间距
默认cell之间是采用分割线进行区分的,如果设置间距,则分割线直接就隐藏了。
.listRowSpacing(10)
该修饰符需要作用在List
组件上,效果如下图,设置了10个间距。
List样式
设置List
样式可以通过下面的代码设置:
.listStyle()
括号内需要传入对应的style
参数。listStyle
修饰符需要作用在List
组件上。
automatic
:默认列表样式,根据设备和环境自动选择合适的列表样式。plain
:普通列表样式。grouped
:分组列表样式。insetGrouped
:缩进分组列表样式。sidebar
:侧边栏列表样式。inset
:缩进列表样式。
以上样式的效果图如下,有些样式不太明显,结合其他的修饰符效果可能更好。
分组显示(Section、 Header、 Footer)
List
组件要想实现分组的功能,很简单,在List
组件中使用Section
组件即可。Section
组件支持Header
和Footer
功能,同时Header
和Footer
也支持直接设置Title
和自定义。
比如下面直接设置Section
title的示例,直接给Title一个字符串,在content闭包内通过ForEach
循环添加要显示的组件,当然也可以不用循环,而是静态数据。
下面是自定义Header
和Footer
的示例,Header
中横向显示了Image
和Text
组件,Footer
中添加了一个Text
,效果如下:
这里额外说一下List
和ForEach
循环的结合使用的场景:
- 如果只是单纯的显示一些静态数据,或者一个数据数据,只用List组件即可。
- 如果要显示数组数据,可以单独使用List组件,将数组数据传给
List
,也可以在List
组件中添加ForEach
组件,通过ForEach
循环遍历数组数据。
var body: some View {
List {
ForEach(fruits, id: \.self) { fruit in
Text(fruit.capitalized)
}
}
}
- 如果即有静态数据又有动态数据要显示,则需
List
和ForEach
组合使用。
上面的代码稍微有些多,主要为了视觉效果更好一些。从效果图中可以看出是人为设置了两个Header
,采用了Text
组件,并设置了相关的属性修饰符。代码中不难看出List
组件里面采用了ForEach
,同时也单独设置了Text
组件。
另外需要提一下,我们给ForEach
设置了前景颜色,这个前景颜色会作用在ForEach
中的每个Text
上。还有给Group
组件设置了List
cell的样式,那么这个样式也会作用在List
中的每个子组件上,这样做代码会更加简洁明了。
添加和删除元素
只能在List
的动态部分添加和删除元素,而静态元素不能在应用程序运行时添加或删除。因此,我们的列表需要完全动态,或者至少包含一个动态部分,以便我们能够添加或删除元素。
为了有添加和删除的入口,我们添加了导航栏,并设置了导航栏的toolbar
,导航栏的设置这里不过多阐述。
添加元素
在导航栏的右侧添加了一个添加元素的按钮,点击后在List
要展示的数据中添加元素,此时也要求该数据必须被@State
包裹修饰,以便其修改后触发UI刷新。具体代码见下面效果图。
删除元素
在导航栏左侧添加了一个EditButton
,点击后将整个List
设置为编辑模式,并在每个cell的左侧提供一个红色的删除按钮。这个过程主要是由系统自动的。不过前提是要对List
添加至少一个可操作的方法,比如onDelete,onMove等,否则点击了EditButton
,List
也没有任何变化。
另外对单个cell进行左滑操作也可以删除元素。
为了实现删除效果,需要在ForEach上添加onDelete方法,如下:
.onDelete(perform: { indexSet in
})
这个onDelete
方法返回一个IndexSet
类型的值,我们可以在这个闭包内进行数据删除,不过为了UI逻辑和业务逻辑低耦合,采用单独抽出来一个方法处理删除逻辑,如下:
func deleteItem(indexSet: IndexSet) {
fruits.remove(atOffsets: indexSet)
}
在deleteItem
方法里面,fruits
数组调用remove(atOffsets:)
方法即可。
因为onDelete
方法返回一个IndexSet
类型的值,所以抽出来的方法同样接受这样类型的值。最终只要在ForEach
上调用下面代码即可,不用额外写传递的参数。
ForEach(fruits, id: \.self) { fruit in
Text(fruit.capitalized)
}
.onDelete(perform: deleteItem)
最终代码及效果如下图:
移动功能
移动功能还是比较普遍的,基于上面的代码,只需要在ForEach
上添加下面的方法即可。
.onMove(perform: { indices, newOffset in
})
这个方法返回两个参数,第一个为IndexSet
类型的值,第二个为Int
类型的值。同onDelete
一样,我们单独抽出来一个方法,同样接收这两个参数。
func moveItem(indexSet: IndexSet, offSet: Int) {
fruits.move(fromOffsets: indexSet, toOffset: offSet)
}
在moveItem
方法中,fruits
数据调用move(fromOffsets:, toOffset:)
方法即可。最后在ForEach
上添加该方法:
ForEach(fruits, id: \.self) { fruit in
Text(fruit.capitalized)
}
.onMove(perform: moveItem)
实现了上面的代码后,我们可以在List
的编辑模式下拖动cell移动,编辑模式下每个cell右侧有三个横杠的图标,拖动即可移动。另外非编辑模式下长按cell后也可以进行拖动,代码及效果图如下:
自定义左滑功能
SwiftUI
的swipeActions()
修饰符允许你添加一个或多个滑动动作按钮到你的列表行,可选地控制他们属于哪一边,以及他们是否应该被触发使用一个完整的滑动。
先看下代码及效果图:
swipeActions()
方法有三个参数,第一个edge
决定操作按钮放哪边;第二个allowsFullSwipe
决定完全滑动是否自动执行第一个操作,默认值为true
;第三个即是内容闭包了。
关于样式,只能通过tint
设置背景色,如果不设置,系统默认是灰色的。
SwiftUI
底层还是很聪明的,比如第一个Button
,我们用Text
显示内容,只有文字,那么系统就显示了Add文字,第二个和第三个用Label
显示,有文字和图片,但是系统聪明的只显示了图片。
对于想要拥有删除功能的按钮,应该使用Button(role: .destructive)
,而不是仅仅指定一个红色背景色,比如上面第三个按钮,不需要有任何删除的逻辑,聪明的底层帮我们实现了。
Button(role: .destructive) {
} label: {
Label("Delete", systemImage: "trash.fill")
}
写在最后
SwiftUI
中的List
确实是一个非常好用的组件,包含的功能也比较多,大多数的App界面也都是由导航栏和List
组成的。
本篇文章篇幅有点长,不过讲的也比较通俗易懂,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。