Swift UI 完全开发(8)List列表
强烈安利有条件的朋友,去京东搜索购买《Swift UI 完全开发》原书,原作者由浅入深,字字珠玑,从真实开发角度介绍了SwiftUI的使用,完整阅读带来的裨益与收获远胜于草草的读完浓缩版博客,个人安利。
本章节将介绍List
列表控件的基础用法,其次为了优化性能使用结构化编程的方式,实现数据抽离,样式结构抽离,通过定义数据模型Model和创建视图组件的方式大大减少代码量。学习List
列表自带的编辑功能,实现拖动排序、滑动删除操作。最后学习List`列表中常用的修饰符
从(6)开始,将不再按照原书顺序介绍,只提供本人阅读后浓缩后代码,并对代码进行逐行解释。
对应《Swift UI 完全开发》原书第8章
- 第一章 第一个SwiftUI项目
- 第二章 Text文字使用
- 第三章 Image图片使用
- 第四章 Stack布局容器使用
- 第五章 Swift语法入门
- 第六章 Button按钮使用
- 第七章 文本输入框使用
- 第八章 List列表
P.S. 文章内容为个人阅读后认为的重点,顺序与原书存在不一致。有任何疑问欢迎评论。
8.1 简单列表创建
- 创建一个新的SwiftUI View,命名为“SwiftUIList”
- 将数据内容定义为数组
sentences
,使用List
列表遍历sentences
数组中的内容。 - 下方代码中,使用
List
列表和ForEach
循环的方式遍历sentences
数组中的内容。ForEach
使用索引遍历sentences
数组,对于每个索引用作每个文本视图的唯一标志符。ForEach
循环的用法类似于for...in...
的逻辑,从sentences
数组取出一个个数后,通过Text
文字根据索引位置item
展示sentences
数组的句子。
struct SwiftUIList: View {
var sentences:[String] = ["item1", "item2", "item3", "item4"]
var body: some View {
List{
ForEach(sentences.indices, id: \.self){
item in
Text(sentences[item])
}
}
}
}
###7.2 数据模型展示列表数据
如果
List
列表中需要创建既包含图片有包含文字的数组呢?
可以通过创建结构体参数类型的方式来实现。
-
新增一个文件夹,命名为“Model”,在创建一个Swift文件,命名为“SentencesModel”。
-
创建所需结构体,
struct SentencesModel
: 定义了一个名为SentencesModel
的结构体。: Identifiable
: 表示SentencesModel
结构体遵循了Identifiable
协议。var id: UUID = UUID()
: 在SentencesModel
结构体内部定义了一个id
属性,其类型为UUID
(通用唯一标识符)。这个属性在初始化时会被自动赋予一个新的、随机的UUID值。var image: String
: 定义了一个String
类型的image
属性,用于存储图片的URL、文件名或其他与图片相关的标识符。var text: String
: 定义了一个String
类型的text
属性,用于存储一段文本或句子。使用这个结构体时,你可以创建多个SentencesModel
的实例,并为它们的image
和text
属性分配不同的值。由于id
属性在初始化时会自动分配一个唯一的UUID,因此每个实例都会有一个唯一的标识符。这在处理列表数据(如SwiftUI中的列表)时特别有用,因为你可以使用这些标识符来追踪和识别列表中的每个数据项。struct SentencesModel: Identifiable{ var id: UUID = UUID() var image: String var text: String }
-
在Assets资源库中,创建一个名为“IconImage”的文件夹,然后导入一批社交账号的图片作为演示素材。
-
回到“SentenceModel”文件中声明一个符合“SentencesModel”结构体的数组定义数据集。
var Sentences:[SentencesModel] = [ SentencesModel(image:"icon_qq", text:"qq"), SentencesModel(image:"icon_wx", text:"微信"), SentencesModel(image:"icon_wb", text:"微博"), ]
-
使用数据模型展示数据。回到SwiftUIList文件中,将之前声明的数组替换成数据模型中声明好的数组。下方代码中,我们替换了
ForEach
循环中的sentences,然后在内容部分使用HStack横向布局容器对Image
图片和Text
文字进行布局。并给整个HStack
横向布局容器增加了边距。struct SwiftUIList: View { var sentences:[SentencesModel] = Sentences var body: some View { List{ ForEach(sentences){ item in HStack(spacing: 20){ Image(item.image) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 32) .clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/) .overlay( Circle() .stroke(Color(.systemGray5), lineWidth:1)) Text(item.text) } .padding(.all,5) } } } }
-
抽离出单独的视图构件。对展示内容进行抽离,搭建一个新的结构体ListItem。通过声明参数并设计样式,形成一个
Image
和Text
的组合组件。然后到SwiftUILitst视图中进行调用,并通过传入赋值的方式,与List
列表和ForEach
遍历Sentences数据中的数据,最后得到我们想要的视图效果。struct SwiftUIList: View { var sentences:[SentencesModel] = Sentences var body: some View { List{ ForEach(sentences){item in ListItem(image: item.image, text: item.text) } } } } //列表组件 struct ListItem:View{ var image: String var text: String var body: some View{ HStack(spacing: 20){ Image(image) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 32) .clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/) .overlay( Circle() .stroke(Color(.systemGray5), lineWidth:1)) Text(text) } .padding(.all,5) } }
8.2 List列表拖动排序和滑动删除
-
添加顶部导航栏。使用
Navigation View
顶部导航栏。下方代码使用“.navigationBarTile()“导航栏标题修饰符给List列表视图增加了一个标题,标题名为“账号中心”,展示方式为居中对齐。值得注意的是,“.navigationBarTitle()“导航栏标题修饰符需要修饰在视图内容上,而不是修饰Navigation View
导航视图。NavigationView{ List{ ForEach(sentences){item in ListItem(image: item.image, text: item.text) } } .navigationBarTitle("账号中心", displayMode: .inline) }
-
添加编辑按钮。SwfitUI 在 List 列表控件控件上封装了一个名为EditButton的按钮,用来快速对列表进行编辑操作,我们将 EditButton 添加到导航视图上。如下方代码:
NavigationView{ List{ ForEach(sentences){item in ListItem(image: item.image, text: item.text) } } .navigationBarTitle("账号中心", displayMode: .inline) .navigationBarItems(trailing: EditButton()) }
-
实现拖动排序方法和滑动删除方法。SwiftUI 提供了排序和滑动删除两种常用操作。下方代码中,我们创建了一个拖动排序的方法 moveItem 。方法中接收单一的 IndexSet 类型的参数,用来定位当前要排序的列的位置,也就拖动时,系统知道了你正在操作的是那条数据。在拖动完成后系统通过Int类型的排序数值来去定存放的位置。
- 这里需要注意的是,操作排序的是List列表中的数据项,而不是List列表本身,因此,使用
.onMove(perform: ... )
修饰符需要作用在ForEach
循环结构上。
struct SwiftUIList: View { @State var sentences:[SentencesModel] = Sentences var body: some View { NavigationView{ List{ ForEach(sentences){item in ListItem(image: item.image, text: item.text) } .onMove(perform:moveItem) } .navigationBarTitle("账号中心", displayMode: .inline) .navigationBarItems(trailing: EditButton()) } } func moveItem(from source:IndexSet, to destination:Int){ sentences.move(fromOffsets: source, toOffset: destination) } }
同理,使用SwiftUI 为 List 列表提供的删除操作,下方代码,deleteRow 删除行的方法中,我们接收单一的IndexSet 类型的参数,用来定位要删除的列的位置;然后调用
remove(atOffsets:...)
方法来删除Messages数组中被定位的特定项。使用方法也和排序类似,使用“.onDelete(perform: …)“修饰符调用删除方法。import SwiftUI struct SwiftUIList: View { @State var sentences:[SentencesModel] = Sentences var body: some View { NavigationView{ List{ ForEach(sentences){item in ListItem(image: item.image, text: item.text) } .onMove(perform:moveItem) .onDelete(perform: deleteRow) } .navigationBarTitle("账号中心", displayMode: .inline) .navigationBarItems(trailing: EditButton()) } } func moveItem(from source:IndexSet, to destination:Int){ sentences.move(fromOffsets: source, toOffset: destination) } func deleteRow(at offsets: IndexSet){ sentences.remove(atOffsets: offsets) } }
- 这里需要注意的是,操作排序的是List列表中的数据项,而不是List列表本身,因此,使用
8.3 修饰符格式化List列表样式
.listStyle()
列表样式修饰符修饰列表。列表!,对列表本身进行修改
-
.listRowSeparator()
列表项分割线修饰符。列表项! -
.listRowBackground()
列表项背景修饰符。列表项!- 添加背景颜色
- 去掉列表背景颜色