listView在APP交互上是一个很高频的容器组件了,在传统的UIKit中一般是给tableview或者collectionView设置delegate,controller实现cell的绘制和UI约束,控制列表的滚动交互和UI风格。在swiftUI中该如何控制list的这些特性,本文做一个简单的记录。
静态的listview
如果要展示的是一些固定数据的且数据量不大的list,简单几行代码就能实现
var body: some View {
List{
Text("Hello, World!")
Text("Hello, World!")
Text("Hello, World!")
}
}
List默认的样式是InsetGroupedListStyle,效果如下
分组的listview
SwiftUI里用Sectoin表示一个分组
Section {
TextField("输入姓名", text: $username)
Toggle(isOn: $isPrivate) {
Text("隐私")
}
Button{}label: {
Text("退出")
}
} header: {
Text("个人")
}
Section {
Slider(value: $fontSize, in: 1...10)
Picker("主题", selection: $appearance) {
Text("Dark").tag(AppearanceStyle.dark)
Text("Light").tag(AppearanceStyle.light)
Text("Auto").tag(AppearanceStyle.auto)
}
} header: {
Text("主题")
}
Section {
HStack{
Text("版本")
Spacer()
Text("1.0.0")
}
} header: {
Text("关于")
}
}
动态的listview
listview的呈现大部分情况下都是根据服务的返回的数据动态表现的,这里mock一个数组模拟api的返回数据,显示一个水果的列表。
struct Food{
var name: String
var icon: String
var isFavorite: Bool
static func preview() -> [Food]{
return [
Food(name: "苹果", icon: "?", isFavorite: true),
Food(name: "草莓", icon: "?", isFavorite: true),
Food(name: "香蕉", icon: "?", isFavorite: true),
Food(name: "樱桃", icon: "?", isFavorite: true),
Food(name: "葡萄", icon: "?", isFavorite: true),
Food(name: "西瓜", icon: "?", isFavorite: true),
]
}
}
直接在List中显示一个arr会报错 「Initializer ‘init(_:rowContent:)’ requires that ‘Food’ conform to ‘Identifiable’」,意思就是你显示的Model不具有唯一性,此时可以提供一个名为id的属性,指定一个Model里的属性作为唯一标识就好了。
List{
let fruits = Food.preview()
List(fruits, id: \.name) { food in
Text(food.name)
}
这里提供的id是fruit的name,mock的数据里是唯一的,但真实的项目里不会用这种性质的字段最为id,一般会给Model通过实现Identifiable的方式扩展一个UUID的id属性来解决。
struct Food: Identifiable{
......
let id = UUID()
}
这个时候你的list中不提供id属性也不会报错了,页面能正常展示
创建自定义cell
SwiftUI里不需要像在UIKit下去创建一个集成UITableViewCell的view,只要在List中指定要显示的UI组件即可,如果UI比较复杂,可以抽取出一个独立的view。
struct FoodRow: View{
let food: Food
var body: some View{
HStack{
Text(food.icon).font(.title)
Text(food.name)
Spacer()
Image(systemName: food.isFavorite ? "heart.fill" : "heart")
}
}
}
设置group样式,以及group的header和footer
新增一个array,分组显示为水果和零食。你可以往List里随意的添加section来实现分组,section有自定义的header和footer属性来显示组的头尾信息,使用默认的InsetGroupedListStyle样式。或者你也可以用DisclosureGroup来显示一个可折叠的分组效果
数据的添加和删除
先说删除,首页显示数据的array标注为@State,其次list有一个delete的方法,传入一个block回调
@inlinable public func onDelete(perform action: ((IndexSet) -> Void)?) -> some DynamicViewContent
添加数据,直接在barItem添加点击事件,往数据里新增一条记录就好
toolbar {
Button {
fruits.append(Food(name: "未知food", icon: "⚠️", isFavorite: false))
} label: {
Text("新增")
}
}