0
点赞
收藏
分享

微信扫一扫

SwiftUI极简教程16:List列表的使用方法进阶学习

今日职言:商业环境的变化是迅速且规律莫测的,你要做的是,一旦决定了就立即去做。

在本章中,我们将基于​​List​​​列表的基本使用方法上,进阶学习​​List​​列表的更多用法。

本章节将分成3个部分讲解。

1、​​onDelete​​​滑动删除和​​onMove​​拖动排序

2、​​ContextMenu​​上下文菜单

3、​​ActionSheets​​弹窗的使用

那我们开始吧。

第一部分:onDelete滑动删除和onMove拖动排序

首先,我们先创建一个新项目,命名为​​SwiftUIList02​​。

SwiftUI极简教程16:List列表的使用方法进阶学习_数组

我们创建一个简单的列表,这里引用之前的​​List​​创建的代码。

完整代码如下:

import SwiftUI

struct Message: Identifiable {
var id = UUID()
var name: String
var image: String
}

// 定义数组,存放数据
var Messages = [ Message(name: "这是微信", image: "weixin"), Message(name: "这是QQ", image: "qq"), Message(name: "这是微博", image: "weibo"), Message(name: "这是手机", image: "phone"), Message(name: "这是邮箱", image: "mail"),]

struct ContentView: View {
var body: some View {

// 列表
List {
ForEach(Messages) { Message in
HStack {
Image(Message.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(Message.name)
.padding()
}
}
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

这里我们使用了​​List​​​列表和​​ForEach​​循环的方法建立了一个列表。

运行后的效果如下:

SwiftUI极简教程16:List列表的使用方法进阶学习_修饰符_02

实现单条​​List​​​列表数据的滑动删除,我们需要调用​​.onDelete(perform:XXXX)​​​修饰符,它是​​ForEach​​的修饰符,用来删除List中的一条条数据;

​perform​​​中引用的是删除的方法,我们定义一个删除方法为​​deleteRow​​,具体实现方法如下:

//滑动删除方法
func deleteRow(at offsets: IndexSet) {
Messages.remove(atOffsets: offsets)
}

在​​deleteRow​​​删除列的方法中,我们接收单一的 ​​IndexSet​​类型的参数,它是用来定位要删除的列的位置的。

然后调用​​remove(atOffsets:XXXX)​​​方法来删除​​Messages​​数组中的被定位的特定项。

这样我们就可以实现列表的滑动删除,运行模拟器,点击一行向右滑动,我们就可以实现删除一行数据。

SwiftUI极简教程16:List列表的使用方法进阶学习_修饰符_03

科普一个知识点。

我们运行的时候会发现,我们滑动删了1行,系统自动又“创建”了一行回来。

这是因为每次我们删除​​List​​​中特定的数据项时,系统会自动更新UI,而更新的数据源是我们创建的​​Messages​​数组。

也就是说,每次删除后,系统重新“​​渲染​​​”页面,但​​Messages​​数组数据没有变化,也就删除了啥,就恢复了啥,就变成了怎么也删除不了了。

我们希望的是,删除了特定的数据项时,​​Messages​​数组的值也需要同步被更改。

因此,我们必须让​​SwiftUI​​​监控属性,并在属性值发生变化时更新UI。我们使用​​@State​​定义数据:

@State var messagesItems

并且将​​ForEach​​​循环的数据源由​​Messages​​​数组,变为我们用​​@State​​​定义的​​messagesItems​​​,同时在​​deleteRow​​​方法中,操作​​remove​​​的对象也换成​​messagesItems​​数组。

这样,我们每次删除数组特定项的时候,​​messagesItems​​​数组就知道我们删除了什么数据,并且“记住”它,在UI刷新的时候,​​ForEach​​​就基于​​messagesItems​​数组内的内容遍历数据。

SwiftUI极简教程16:List列表的使用方法进阶学习_数据_04

好了,我们实现了​​List​​页面的滑动删除操作了。

再补充一个知识点。

如果我们给​​List​​​列表创建导航栏,还可以使用​​SwiftUI​​​已经封装好的​​EditButton​​​编辑按钮,从而实现列表快速进入编辑状态,这在​​Apple​​自家的备忘录中可以看到。

当我们点击​​Edit​​​按钮时,按钮文字会变成​​done​​​,同时​​List​​列表都变成可删除的模式。

NavigationView {

// 列表
List {
ForEach(messagesItems) { Message in
HStack {
Image(Message.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(Message.name)
.padding()
}
}.onDelete(perform: deleteRow)
}.navigationBarItems(trailing: EditButton())
}

SwiftUI极简教程16:List列表的使用方法进阶学习_数组_05

再延伸地补充一个知识点。

​List​​​列表编辑模式下,除了删除之前,还可以针对于数据项进行拖动排序,我们用的是​​.onMove(perform:XXXX)​​​修饰符,它也是​​ForEach​​​的修饰符,用来实现​​List​​列表的拖动单条数据的改变它的排序顺序;

NavigationView {

// 列表
List {
ForEach(messagesItems) { Message in
HStack {
Image(Message.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(Message.name)
.padding()
}
}
.onDelete(perform: deleteRow)
.onMove(perform: moveItem)
}.navigationBarItems(trailing: EditButton())
}

​perform​​​中引用的是排序的方法,我们定义一个删除方法为​​moveItem​​,具体实现方法如下:

// 拖动排序方法
func moveItem(from source: IndexSet, to destination: Int) {
messagesItems.move(fromOffsets: source, toOffset: destination)
}

同样,我们接收单一的 ​​IndexSet​​类型的参数,它是用来定位要排序的列的位置。

然后它的排序数值为​​Int​​​类型,简单来说,初始的排序是​​0、1、2、3、4​​​,假设我们把​​3​​​拖动到​​0​​,那么系统将自动更新重新排列顺序。

从而实现排序的效果。

SwiftUI极简教程16:List列表的使用方法进阶学习_数据_06

快来试试吧!

第二部分:ContextMenu上下文菜单

​ContentMenu​​​上下文菜单是​​iOS13​​引用的一个新功能,效果为长按列表时,弹出一个悬浮窗口,用于快捷操作。

​iPhone​​当中存在大量这样的交互,示例:长按系统设置,打开快捷操作弹窗。

SwiftUI极简教程16:List列表的使用方法进阶学习_数据_07

实现​​ContentMenu​​​上下文菜单的方法也很简单,使用​​.contentMenu​​修饰符,在修饰符里面构建需要展示或者操作的内容。

这里我们尝试做一个长按删除的操作,长按列表的一个项目,弹出上下文​​ContentMenu​​菜单,里面是一个删除按钮,点击按钮,删除指定行的数据。

.contextMenu {
Button(action: {
// 点击删除
}) {
HStack {
Text("删除")
Image(systemName: "trash")
}
}
}

SwiftUI极简教程16:List列表的使用方法进阶学习_修饰符_08

然后,我们实现下删除的操作,会有些复杂,请耐心查阅。

它不像我们使用​​ForEach​​​使用的​​.onDelete​​​删除修饰符的方法不一样,​​ContentMenu​​上下文菜单没有索引定位到特定的数据项,也就不知道我们点击选中的是哪一条数据。

这就需要我们自己定位到​​Messages​​​数组里面的​​Message​​​的​​id​​​,这样我们就可以通过​​id​​定位到是数组中的哪一条数据了。

方法如下:

//删除的方法
func delete(item Message: Message) {
if let index = self.messagesItems.firstIndex(where: { $0.id == Message.id }) {
self.messagesItems.remove(at: index)
}
}

我们定义了一个​​delete​​​的函数方法,接收了一个​​Message​​​对象,并在​​Messages​​​数组中搜索​​Message​​对象的索引。

为了找到​​Message​​​对象的索引,我们调用​​firstIndex​​​函数循环遍历数组,并将给定​​Messages​​​的​​id​​​与数组中的​​id​​进行比较。

如果有​​id​​​一样,则​​firstIndex​​​函数返回​​Messages​​数组的索引。

这样我们就知道长按的是哪一条数据了!

接下来,我们就可以通过调用​​remove(at:XXXX)​​​修饰符从​​Messages​​数组中删除对应的数据项。

self.delete(item: Message)

SwiftUI极简教程16:List列表的使用方法进阶学习_数据_09

我们运行一下模拟器,长按,系统会给出​​ContentMenu​​上下文菜单,它是一个删除按钮,我们点击删除按钮,该行数据就被删除了。

完整代码如下:

import SwiftUI

struct Message: Identifiable {
var id = UUID()
var name: String
var image: String
}

// 定义数组,存放数据
var Messages = [ Message(name: "这是微信", image: "weixin"), Message(name: "这是QQ", image: "qq"), Message(name: "这是微博", image: "weibo"), Message(name: "这是手机", image: "phone"), Message(name: "这是邮箱", image: "mail"),]

struct ContentView: View {

// 定义数组
@State var messagesItems = Messages

var body: some View {
NavigationView {

// 列表
List {
ForEach(messagesItems) { Message in
HStack {
Image(Message.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(Message.name)
.padding()
}
.contextMenu {
Button(action: {
// 点击删除
self.delete(item: Message)
}) {
HStack {
Text("删除")
Image(systemName: "trash")
}
}
}
}
.onDelete(perform: deleteRow)
.onMove(perform: moveItem)
}.navigationBarItems(trailing: EditButton())
}
}

//删除的方法
func delete(item Message: Message) {
if let index = self.messagesItems.firstIndex(where: { $0.id == Message.id }) {
self.messagesItems.remove(at: index)
}
}

// 滑动删除方法
func deleteRow(at offsets: IndexSet) {
messagesItems.remove(atOffsets: offsets)
}

// 拖动排序方法
func moveItem(from source: IndexSet, to destination: Int) {
messagesItems.move(fromOffsets: source, toOffset: destination)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

SwiftUI极简教程16:List列表的使用方法进阶学习_修饰符_10

第三部分:ActionSheets弹窗的使用

弹窗当中,我们在之前的章节学习了​​ModelView​​​弹窗,这里我们再拓展一种弹窗模式,叫做​​.actionSheet​​。

它算是​​.sheet​​​的另一种形式,常用在用户敏感操作的二次确认,但又不像​​Alerts​​警告弹窗那么严肃,属于一般强调。

SwiftUI极简教程16:List列表的使用方法进阶学习_数据_11

​.actionSheet​​​和​​Alerts​​警告弹窗的实现方式大体相同。

.actionSheet(isPresented:$showActionSheet) {
//ActionSheet结构体

我们还是使用​​isPresented​​​触发,我们定义一个变量​​showActionSheet​​​的状态,初始状态是​​false​​。

@State var  showActionSheet = false

接下来,我们实现下​​ActionSheet​​结构体:

// ActionSheet弹窗
.actionSheet(isPresented: self.$showActionSheet) {
ActionSheet(
title: Text("你确定要删除此项吗?"),
message: nil,
buttons: [
.destructive(Text("删除"), action: {
//点击删除
}),
.cancel(Text("取消")
])
}

SwiftUI极简教程16:List列表的使用方法进阶学习_SwiftUI_12

下面,我们做一个“有趣”的操作。

我们承接上一部分的内容,长按列表,弹出一个​​ContentMenu​​​上下文菜单,里面是一个删除按钮,点击删除按钮,打开​​ActionSheet​​​弹窗,里面又是一个删除按钮,点击​​ActionSheet​​弹窗内的删除按钮,删除列表的数据项。

SwiftUI极简教程16:List列表的使用方法进阶学习_数组_13

我们就实现了基于​​ActionSheet​​弹窗的删除操作啦!

完整代码如下:

import SwiftUI

struct Message: Identifiable {
var id = UUID()
var name: String
var image: String
}

// 定义数组,存放数据
var Messages = [
Message(name: "这是微信", image: "weixin"),
Message(name: "这是QQ", image: "qq"),
Message(name: "这是微博", image: "weibo"),
Message(name: "这是手机", image: "phone"),
Message(name: "这是邮箱", image: "mail"),
]

struct ContentView: View {

// 定义数组
@State var messagesItems = Messages
@State var showActionSheet = false

var body: some View {
NavigationView {

// 列表
List {
ForEach(messagesItems) { Message in
HStack {
Image(Message.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(Message.name)
.padding()
}

// 上下文菜单
.contextMenu {
Button(action: {
// 点击打开ActionSheet弹窗
self.showActionSheet.toggle()

}) {
HStack {
Text("删除")
Image(systemName: "trash")
}
}
}

// ActionSheet弹窗
.actionSheet(isPresented: self.$showActionSheet) {
ActionSheet(
title: Text("你确定要删除此项吗?"),
message: nil,
buttons: [
.destructive(Text("删除"), action: {
//点击删除
self.delete(item: Message)
}),
.cancel(Text("取消"))
])
}
}
.onDelete(perform: deleteRow)
.onMove(perform: moveItem)
}.navigationBarItems(trailing: EditButton())
}
}

// 删除的方法
func delete(item Message: Message) {
if let index = messagesItems.firstIndex(where: { $0.id == Message.id }) {
messagesItems.remove(at: index)
}
}

// 滑动删除方法
func deleteRow(at offsets: IndexSet) {
messagesItems.remove(atOffsets: offsets)
}

// 拖动排序方法
func moveItem(from source: IndexSet, to destination: Int) {
messagesItems.move(fromOffsets: source, toOffset: destination)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

快来动手试试吧!

如果本专栏对你有帮助,不妨点赞、评论、关注~

举报

相关推荐

0 条评论