SwiftUI中List的样式及使用详解(添加、移动、删除、自定义滑动)

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组件上,则没有任何效果。

swift 复制代码
.listRowBackground(Color.blue)

内间距

另外上面的代码中,Text的宽度没有完全和cell的宽度对齐,两边还有一些间距,有的时候是不需要这个间距的,设置下面代码即可,同样是要修饰在List闭包内的组件上。

swift 复制代码
.listRowInsets(EdgeInsets())


分割线颜色与隐藏

设置分割线以及是否显示需要将下面两个修饰符作用在List闭包内的组件上。

swift 复制代码
// 隐藏分割线,默认是显示的。
.listRowSeparator(.hidden)
// 设置分割线颜色。
.listRowSeparatorTint(.red)

设置Cell之间的间距

默认cell之间是采用分割线进行区分的,如果设置间距,则分割线直接就隐藏了。

swift 复制代码
.listRowSpacing(10)

该修饰符需要作用在List组件上,效果如下图,设置了10个间距。

List样式

设置List样式可以通过下面的代码设置:

swift 复制代码
.listStyle()

括号内需要传入对应的style参数。listStyle修饰符需要作用在List组件上。

  1. automatic:默认列表样式,根据设备和环境自动选择合适的列表样式。
  2. plain:普通列表样式。
  3. grouped:分组列表样式。
  4. insetGrouped:缩进分组列表样式。
  5. sidebar:侧边栏列表样式。
  6. inset:缩进列表样式。

以上样式的效果图如下,有些样式不太明显,结合其他的修饰符效果可能更好。

分组显示(Section、 Header、 Footer)

List组件要想实现分组的功能,很简单,在List组件中使用Section组件即可。Section组件支持HeaderFooter功能,同时HeaderFooter也支持直接设置Title和自定义。

比如下面直接设置Section title的示例,直接给Title一个字符串,在content闭包内通过ForEach循环添加要显示的组件,当然也可以不用循环,而是静态数据。

下面是自定义HeaderFooter的示例,Header中横向显示了ImageText组件,Footer中添加了一个Text,效果如下:

这里额外说一下ListForEach循环的结合使用的场景:

  1. 如果只是单纯的显示一些静态数据,或者一个数据数据,只用List组件即可。
  2. 如果要显示数组数据,可以单独使用List组件,将数组数据传给List,也可以在List组件中添加ForEach组件,通过ForEach循环遍历数组数据。
swift 复制代码
  var body: some View {
    List {
      ForEach(fruits, id: \.self) { fruit in
        Text(fruit.capitalized)
      }
    }
  }
  1. 如果即有静态数据又有动态数据要显示,则需ListForEach组合使用。

    上面的代码稍微有些多,主要为了视觉效果更好一些。从效果图中可以看出是人为设置了两个Header,采用了Text组件,并设置了相关的属性修饰符。代码中不难看出List组件里面采用了ForEach,同时也单独设置了Text组件。

另外需要提一下,我们给ForEach设置了前景颜色,这个前景颜色会作用在ForEach中的每个Text上。还有给Group组件设置了List cell的样式,那么这个样式也会作用在List中的每个子组件上,这样做代码会更加简洁明了。

添加和删除元素

只能在List的动态部分添加和删除元素,而静态元素不能在应用程序运行时添加或删除。因此,我们的列表需要完全动态,或者至少包含一个动态部分,以便我们能够添加或删除元素。

为了有添加和删除的入口,我们添加了导航栏,并设置了导航栏的toolbar,导航栏的设置这里不过多阐述。

添加元素

在导航栏的右侧添加了一个添加元素的按钮,点击后在List要展示的数据中添加元素,此时也要求该数据必须被@State包裹修饰,以便其修改后触发UI刷新。具体代码见下面效果图。

删除元素

在导航栏左侧添加了一个EditButton,点击后将整个List设置为编辑模式,并在每个cell的左侧提供一个红色的删除按钮。这个过程主要是由系统自动的。不过前提是要对List添加至少一个可操作的方法,比如onDelete,onMove等,否则点击了EditButtonList也没有任何变化。

另外对单个cell进行左滑操作也可以删除元素。

为了实现删除效果,需要在ForEach上添加onDelete方法,如下:

swift 复制代码
.onDelete(perform: { indexSet in
                        
})

这个onDelete方法返回一个IndexSet类型的值,我们可以在这个闭包内进行数据删除,不过为了UI逻辑和业务逻辑低耦合,采用单独抽出来一个方法处理删除逻辑,如下:

swift 复制代码
func deleteItem(indexSet: IndexSet) {
  fruits.remove(atOffsets: indexSet)
}

deleteItem方法里面,fruits数组调用remove(atOffsets:)方法即可。

因为onDelete方法返回一个IndexSet类型的值,所以抽出来的方法同样接受这样类型的值。最终只要在ForEach上调用下面代码即可,不用额外写传递的参数。

swift 复制代码
ForEach(fruits, id: \.self) { fruit in
	Text(fruit.capitalized)
}
.onDelete(perform: deleteItem)

最终代码及效果如下图:

移动功能

移动功能还是比较普遍的,基于上面的代码,只需要在ForEach上添加下面的方法即可。

swift 复制代码
.onMove(perform: { indices, newOffset in

})

这个方法返回两个参数,第一个为IndexSet类型的值,第二个为Int类型的值。同onDelete一样,我们单独抽出来一个方法,同样接收这两个参数。

swift 复制代码
func moveItem(indexSet: IndexSet, offSet: Int) {
  fruits.move(fromOffsets: indexSet, toOffset: offSet)
}

moveItem方法中,fruits数据调用move(fromOffsets:, toOffset:)方法即可。最后在ForEach上添加该方法:

swift 复制代码
ForEach(fruits, id: \.self) { fruit in
	Text(fruit.capitalized)
}
.onMove(perform: moveItem)

实现了上面的代码后,我们可以在List的编辑模式下拖动cell移动,编辑模式下每个cell右侧有三个横杠的图标,拖动即可移动。另外非编辑模式下长按cell后也可以进行拖动,代码及效果图如下:

自定义左滑功能

SwiftUIswipeActions()修饰符允许你添加一个或多个滑动动作按钮到你的列表行,可选地控制他们属于哪一边,以及他们是否应该被触发使用一个完整的滑动。

先看下代码及效果图:

swipeActions()方法有三个参数,第一个edge决定操作按钮放哪边;第二个allowsFullSwipe决定完全滑动是否自动执行第一个操作,默认值为true;第三个即是内容闭包了。

关于样式,只能通过tint设置背景色,如果不设置,系统默认是灰色的。
SwiftUI底层还是很聪明的,比如第一个Button,我们用Text显示内容,只有文字,那么系统就显示了Add文字,第二个和第三个用Label显示,有文字和图片,但是系统聪明的只显示了图片。

对于想要拥有删除功能的按钮,应该使用Button(role: .destructive),而不是仅仅指定一个红色背景色,比如上面第三个按钮,不需要有任何删除的逻辑,聪明的底层帮我们实现了。

swift 复制代码
Button(role: .destructive) {

} label: {
  Label("Delete", systemImage: "trash.fill")
}

写在最后

SwiftUI中的List确实是一个非常好用的组件,包含的功能也比较多,大多数的App界面也都是由导航栏和List组成的。

本篇文章篇幅有点长,不过讲的也比较通俗易懂,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

相关推荐
HarderCoder7 小时前
iOS 知识积累第一弹:从 struct 到 APP 生命周期的全景复盘
ios
叽哥18 小时前
Flutter Riverpod上手指南
android·flutter·ios
用户092 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan2 天前
iOS26适配指南之UIColor
ios·swift
权咚2 天前
阿权的开发经验小集
git·ios·xcode
用户092 天前
TipKit与CloudKit同步完全指南
ios·swift
法的空间3 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_915918413 天前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview
凯子坚持 c3 天前
精通 Redis list:使用 redis-plus-plus 的现代 C++ 实践深度解析
c++·redis·list
00后程序员张3 天前
iOS App 混淆与加固对比 源码混淆与ipa文件混淆的区别、iOS代码保护与应用安全场景最佳实践
android·安全·ios·小程序·uni-app·iphone·webview