List
- List具体解说
- static List
- Dynamic List
- 删去List中的cell
- 移动List中的cell
- List增加Section
- 设置cell的布景色彩
- 经过listStyle创立List
- 创立可折叠的List
- 滚动到list中的特定cell
- List支撑多选
- List的分隔符的躲藏和色彩
- List的下拉改写
- Cell增加自界说侧滑Button
- List的Binding
概述
文章首要共享SwiftUI Modifier的学习过程,将运用事例的办法进行阐明。内容浅显易懂,List展现部分调试成果,不过测验代码是齐全的。假如想要运行成果,能够移步Github下载code -> github事例链接
1、List具体解说
SwiftUI的List
视图类似于UITableView,能够依据需求显现静态的列表视图。比照于UITableView运用也更加简单,不需求经过Storyboard或许在代码中register,也不需求告知它有多少行,也不需求去手动排列和装备cell。
相反,SwiftUI的List是为可组合性而规划的–规划成能够从小的东西构建更大的东西。所以,SwiftUI没有一个大的视图操控器,而是经过构建小的视图进行堆叠。
就代码大小而言,假如不考虑其他要素,差异是惊人的–能够删去几乎一切幻想中的UITableView的视图代码,但仍然能得到那种很好的UI和感觉。
List {
Text("Hello, World!")
}
调试成果
仅3行代码就完结了UITableView的作业。
2、创立static List
要创立项目的静态列表
,首先要界说列表中每一行应该是什么样子。这是一个与其他View相同的View,方案在行中显现的数据供给任何参数。一旦有了row,就能够创立一个List视图,该视图能够依据需求创立任意多的行。
struct Pizzeria: View {
let name: String
var body: some View {
Text("Restaurant: \(name)")
}
}
struct FFListStaticitems: View {
var body: some View {
//例如,界说一个Pizzeria视图,它将显现一个名称字符串,然后将其用作带有三个固定数据快的Lists的一行。
List {
Pizzeria(name: "Joe's Original")
Pizzeria(name: "The Real Joe's Original")
Pizzeria(name: "Original Joe's")
}
}
}
调试成果
3、创立Dynamic List
为了处理动态项,有必要告知SwiftUI它怎么辨认哪个item是哪个。这能够经过手动指定标识特点或运用可辨认的协议(Identifiable
)来完结。协议只需一个要求,即类型有必要具有某种类型的id
值,SwiftUI能够运用id来标记。
struct Restaurant: Identifiable {
let id = UUID()
let name: String
}
struct RestaurantRow: View {
let restaurant: Restaurant
var body: some View {
Text("Come and eat at \(restaurant.name)")
}
}
struct FFListDynamicItems: View {
let restaurants = [
Restaurant(name: "Joe's Original"),
Restaurant(name: "The Real Joe's Original"),
Restaurant(name: "Original Joe's")
]
var body: some View {
List(restaurants) { restaurant in
RestaurantRow(restaurant: restaurant)
}
}
//上面大部分的操作都是预备数据,最终一步才是真实的操作,运用List(restaurants)从restaurants数组
//创立一个列表,对数组中的每个item履行一次闭包,经过RestaurantRow(restaurant: restaurant)创立cell
}
4、删去List中的cell
SwiftUI供给了deleteDisabled()
修饰符能够从列表中删去cell。假如只想滑动从数组中删去项,而不增加任何额定的逻辑,那么简单的办法非常有用。运用与列表的数据绑定,并传入editActions
参数
4.1、基础侧滑删去
@State private var users = ["Glenn", "Malcolm", "Nicola", "Terri"]
//能够当即滑动来删去行,而且用户数组将更新,假如想移动cell,将.delete 改成.all
List($users, id: \.self, editActions: .delete) { $user in
Text(user)
}
4.2、deleteDisabled()条件删去
@State private var users = ["Glenn", "Malcolm", "Nicola", "Terri"]
//删去也能够运用deleteDisabled()和任何条件。例如,用户能够随意的删去,但至少满意row > 1
List($users, id: \.self, editActions: .delete) { $user in
Text(user)
.deleteDisabled(users.count < 2)
}
4.3、onDelete删去
通常会想要调用Swift的remove(at Offsets:)
办法从你的序列中删去行。因为SwiftUI正在监视你的状态,你所做的任何更改都会主动反应在你的UI中。例如,创立一个包含三个项目的ContentView结构体,然后附加一个onDelete(perform:)
修饰符,从列表中删去row
@State private var users1 = ["BBlv", "Taylor", "Paul"]
//对于更杂乱的删去办法,将onDelete(perform:)修饰符附加到列表中的ForEach,并让它发生在删去操作时调用这个办法,这个处理需求有一个特定的函数来承受多个索引来删去
func delete(at offsets: IndexSet) {
users1.remove(atOffsets: offsets)
}
List {
ForEach(users1, id: \.self) { user in
Text(user)
}
.onDelete(perform: { indexSet in
delete(at: indexSet)
})
.navigationTitle("Users")
}
onDelete()作为ForEach的修饰符,不能直接用于List,因为此修饰符是在DynamicViewContent中界说的
5、移动List中的cell
SwiftUI的moveDisabled()
修饰符能够移动行,类似于deleteDisabled()修饰符
struct FFListMoveRows: View {
@State private var users = ["Glenn", "Malcolm", "Nicola", "Terri"]
@State private var usres1 = ["BBlv", "Taylor", "Paul"]
var body: some View {
List($users, id: \.self, editActions: .move) { $user in
Text(user)
}
//经过moveDisable()修饰符和条件来移动rows
List($users, id: \.self, editActions: .move) { $user in
Text(user)
.moveDisabled(user == "Glenn")
}
NavigationStack {
List {
ForEach(users, id: \.self) { user in
Text(user)
}
.onMove(perform: { indices, newOffset in
move(from: indices, to: newOffset)
})
}
.toolbar {
EditButton()
}
}
}
//对于更杂乱的move办法,将onMove(preform:)修饰符附加到list中
func move(from source: IndexSet, to destination: Int) {
users.move(fromOffsets: source, toOffset: destination)
}
}
6、List增加Section
创立一个cell备用
struct TaskRow: View {
var body: some View {
Text("Task data goes here")
}
}
6.1、创立附加header的section
List {
Section {
TaskRow()
TaskRow()
TaskRow()
} header: {
Text("Important tasks")
}
Section {
TaskRow()
TaskRow()
TaskRow()
} header: {
Text("Others tasks")
}
}
6.2、创立附加footer的section
Section {
Text("Row 1")
Text("Row 2")
Text("Row 3")
} footer: {
Text("End")
}
6.3、headerProminence()修正section的header
默许情况下,section header匹配默许的iOS样式,能够经过headerProminence()
修饰符更改
Section {
TaskRow()
} header: {
Text("Header")
}
.headerProminence(.increased)
调试成果
7、设置cell的布景色彩
SwiftUI有一个专门的修饰符来设置cell的布景视图,是listRowBackground()
。它承受任何类型的视图–包括色彩、图像和形状。
struct SwiftListListRowBackground: View {
var body: some View {
List {
ForEach(0..<10) {
Text("Row \($0)")
}
.listRowBackground(Color.green)
}
}
}
调试成果
8、经过listStyle创立List
SwiftUI的列表视图有一个listStyle()
修饰符来操控list的样式:
- grouped
- insetGrouped
struct ExampleRow: View {
var body: some View {
Text("Example Row")
}
}
struct FFListInsetGroup: View {
var body: some View {
//.grouped
List {
Section {
ExampleRow()
ExampleRow()
ExampleRow()
} header: {
Text("Examples")
}
}
.listStyle(.grouped)
//insetGrouped
List(0..<100) { i in
Text("Row \(i)")
}
.listStyle(.insetGrouped)
}
}
9、创立可折叠的List
SwiftUI的List视图有一个增强初始化器,能够创立带有子节点的扩展
–以能够点击的箭头的办法出现,当点击时,箭头转动,显现子节点。要运用这种方法的List,需求有准确的数据,数据模型应该拥有相同类型的可选子节点,这样能够创立树。通常情况下,可能会从JSON或类似的地方加载这类数据。
struct Bookmark: Identifiable {
let id = UUID()
let name: String
let icon: String
let items: [Bookmark]?
static let apple = Bookmark(name: "Apple", icon: "1.circle", items: nil)
static let bbc = Bookmark(name: "BBC", icon: "square.and.pencil", items: nil)
static let swift = Bookmark(name: "Swift", icon: "bolt.fill", items: nil)
static let twitter = Bookmark(name: "Twitter", icon: "mic", items: nil)
static let example1 = Bookmark(name: "Favorites", icon: "star", items: [Bookmark.swift, Bookmark.apple, Bookmark.bbc, Bookmark.swift, Bookmark.twitter])
static let example2 = Bookmark(name: "Recent", icon: "timer", items: [Bookmark.apple, Bookmark.bbc, Bookmark.twitter])
static let example3 = Bookmark(name: "Recommended", icon: "hand.thumbsup", items: [Bookmark.apple, Bookmark.swift, Bookmark.bbc, Bookmark.twitter])
}
struct FFListExpanding: View {
let items: [Bookmark] = [.example1, .example2, .example3]
var body: some View {
List(items, children: \.items) { row in
HStack {
Image(systemName: row.icon)
Text(row.name)
}
}
}
}
调试成果
10、滚动到list中的特定cell
让SwiftUI的List移动来显现特定的行。应该把它嵌入到ScrollViewReader
中。这在其代理上供给了一个scrollTo()
办法,该办法能够移动到列表中的任意行,只需求供给id和锚点。
10.1、scrollTo
ScrollViewReader(content: { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50)
}
List(0..<100, id: \.self) { i in
Text("Example \(i)")
.id(i)
}
}
})
10.2、anchor
将锚点标记为.top
ScrollViewReader(content: { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50, anchor: .top)
}
List(0..<100, id: \.self) { i in
Text("Example \(i)")
.id(i)
}
}
})
10.3、withAnimation
将锚点标记为.top,以动画作用过渡
ScrollViewReader(content: { proxy in
VStack {
Button("Jump to #50") {
withAnimation {
proxy.scrollTo(50, anchor: .top)
}
}
List(0..<100, id: \.self) { i in
Text("Example \(i)")
.id(i)
}
}
})
11、List支撑多选
SwiftUI的list支撑多选,但仅限于修正形式之下。为了支撑多选,首先增加一个与列表中运用的类型相同的可选特点调集
。例如,假如一个整数list,那么将有一个可选的Int。运用他的选择参数将它传递给list,确保list处于修正形式。
struct FFListSelectionRow: View {
@State private var selection = Set<String>()
let names = ["BBLv", "Cyril", "Lana", "Mallory", "Sterling"]
var body: some View {
NavigationStack {
List(names, id: \.self, selection: $selection) { name in
Text(name)
}
.navigationTitle("List Selection")
.toolbar {
EditButton()
}
}
}
}
12、List的分隔符的躲藏和色彩
SwiftUI供给了两个修饰符来操控其列表中行分隔符的外观:
-
listRowSeparator()
用于操控行分隔符是否可见, -
listRowSeparatorTint()
用于操控分隔符的色彩
struct FFListSeparator: View {
var body: some View {
//躲藏行分隔符
List {
ForEach(1..<100) { index in
Text("Row \(index)")
.listRowSeparator(.hidden)
}
}
//设置分隔符色彩
List {
ForEach(1..<100) { index in
Text("Row \(index)")
.listRowSeparatorTint(.red)
}
}
}
}
13、List的下拉改写
SwiftU的refreshable()
修饰符能够附加到list上,当发生拖拽且有一段距离时触发。只需的你的代码完结运行,iOS会主动显现一个菊花。
struct NewsItem: Decodable, Identifiable {
let id: Int
let title: String
let strap: String
}
struct FFListPullRefresh: View {
@State private var news = [
NewsItem(id: 0, title: "Want the latest news?", strap: "Pull to refresh!")
]
var body: some View {
NavigationStack {
List(news) { item in
VStack(alignment: .leading) {
Text(item.title)
.font(.headline)
Text(item.strap)
.foregroundStyle(.secondary)
}
}
.refreshable {
do {
let url = URL(string: "https://www.hackingwithswift.com/samples/news-1.json")!
let (data, _) = try await URLSession.shared.data(from: url)
news = try JSONDecoder().decode([NewsItem].self, from: data)
} catch {
news = []
}
}
}
}
}
调试成果
因为加载的速度有一点快,中间菊花没截到。
14、Cell增加自界说侧滑Button
SwiftUI的swipeActions()
修饰符能够增加一个或多个滑动button到cell,可选的操控它们归于哪一边,以及它们是否应该被触发运用一个完整的滑动。
14.1、swipeActions
button运用了tint
进行了作色,假如未运用,默许是灰色的
Text("Pepperoni pizza")
.swipeActions {
Button("Order") {
print("Awesome!")
}
.tint(.green)
}
Text("pepperoni with pineapple")
.swipeActions {
Button("Burn") {
print("Right on")
}
.tint(.red)
}
14.2、allowsFullSwipe
默许情况下,假如滑动满足远,将主动触发第一个滑动动作。假如想要禁用,在创立滑动时将allowsFullSwipe
设置为false。
let friends = ["Antoine", "Bas", "Curt", "Dave", "Erica"]
ForEach(friends, id: \.self) { friend in
Text(friend)
.swipeActions(allowsFullSwipe:false) {
Button {
print("Muting conversation")
} label: {
Label("Mute", systemImage: "bell.slash.fill")
}
.tint(.indigo)
Button(role: .destructive) {
print("Deleting conversation")
} label: {
Label("Delete", systemImage: "trash.fill")
}
}
}
对于真实删去性质的按钮,应该经过role
设定,避免经过tint指定赤色
14.3、swipeActions(edge: .leading)
假如想要在cell的两端都增加不同的滑动操作,只需求了用两次swipeActions在边缘
@State private var total = 0
Section {
ForEach(1..<100) { i in
Text("\(i)")
.swipeActions(edge: .leading) {
Button {
total += i
} label: {
Label("Add \(i)", systemImage: "plus.circle")
}
.tint(.indigo)
}
.swipeActions(edge: .trailing) {
Button {
total -= i
} label: {
Label("Subtract \(i)", systemImage: "minus,circle")
}
}
}
} header: {
Text("Total: \(total)")
}
.headerProminence(.increased)
调试成果
15、List的Binding
SwiftUI能够直接从绑定中创立List或ForEach,然后供给内容闭包,显现的数据中的每个元素都供给独自的绑定
。
struct UserA: Identifiable {
let id = UUID()
let name: String
var isContacted = false
}
struct FFListForEach: View {
@State private var users = [
UserA(name: "Taylor"),
UserA(name: "Justin"),
UserA(name: "Adele")
]
var body: some View {
List($users) { $user in
HStack {
Text(user.name)
Spacer()
Toggle("User has been contacted", isOn: $user.isContacted)
.labelsHidden()
}
}
}
}
以这种办法运用绑定是修正列表的最有用办法,因为他不会在只需一个项更改时导致整个视图从头加载