杭州拱墅區(qū)做網(wǎng)站公眾號運(yùn)營
SwiftUI中的List可是個好東西,它用于顯示可滾動列表的視圖容器,類似于UITableView。在List中可以顯示靜態(tài)或動態(tài)的數(shù)據(jù),并支持垂直滾動。List是一個數(shù)據(jù)驅(qū)動的視圖,當(dāng)數(shù)據(jù)發(fā)生變化時,列表會自動更新。針對List,我們還可以進(jìn)行添加、移動、刪除以及滑動等功能。示例代碼:
//
// RouterView.swift
// SwiftBook
//
// Created by song on 2024/7/4.
//import SwiftUIstruct RouterView: View {@State var monthes = ["January","February","March","April","May","June","July","August","September","October","November","December"]var body: some View {List(monthes, id: \.self) { m inText(m)}}
}#Preview {RouterView()
}
顯示靜態(tài)數(shù)據(jù)
顯示靜態(tài)的數(shù)據(jù)還是比較簡單的,比如要顯示12個月份,代碼中,直接在List組件里面放置Text組件即可,不過如果同類型數(shù)據(jù)過多,還是采用動態(tài)數(shù)組的方式去顯示。
顯示動態(tài)數(shù)據(jù)
很多的時候采用的都是動態(tài)數(shù)組顯示的方式,比如通過網(wǎng)絡(luò)請求回來的數(shù)據(jù),再通過List顯示出來等等:
上面的代碼簡化了很多,我們將months數(shù)據(jù)作為第一個參數(shù),傳給了List,而List的第二個參數(shù)id,則表示months數(shù)據(jù)中的元素采用自身作為唯一的標(biāo)識符。如果數(shù)組中的兩個或多個元素不是唯一的,這可能會導(dǎo)致問題。
如果不用字符串?dāng)?shù)據(jù),而是該用了實(shí)例對象數(shù)組,則可以將實(shí)例對象的結(jié)構(gòu)實(shí)現(xiàn)Identifiable協(xié)議,該協(xié)議需要實(shí)現(xiàn)一個id屬性,如果將一個實(shí)現(xiàn)了Identifiable協(xié)議的實(shí)例對象數(shù)組傳給List,則不需要指定id參數(shù)了。比如下面的代碼示例:
樣式設(shè)置
默認(rèn)的List是有背景色,Light模式下是白色,Dark模式下是灰色,比如下面這個代碼:?
背景色
只需要添加一行代碼即可,將其修飾在List
閉包內(nèi)部的組件上,即修飾在Text
組件上。如果直接修飾在List
組件上,則沒有任何效果。?
內(nèi)間距
另外上面的代碼中,Text的寬度沒有完全和cell的寬度對齊,兩邊還有一些間距,有的時候是不需要這個間距的,設(shè)置下面代碼即可,同樣是要修飾在List閉包內(nèi)的組件上:
分割線顏色與隱藏
設(shè)置分割線以及是否顯示需要將下面兩個修飾符作用在List閉包內(nèi)的組件上。
// 隱藏分割線,默認(rèn)是顯示的。
.listRowSeparator(.hidden)
// 設(shè)置分割線顏色。
.listRowSeparatorTint(.red)
設(shè)置Cell之間的間距
默認(rèn)cell之間是采用分割線進(jìn)行區(qū)分的,如果設(shè)置間距,則分割線直接就隱藏了。該修飾符需要作用在List
組件上,效果如下圖,設(shè)置了10個間距:
List樣式
設(shè)置List樣式可以通過在括號內(nèi)需要傳入對應(yīng)的style參數(shù)。listStyle修飾符需要作用在List組件上。
automatic:默認(rèn)列表樣式,根據(jù)設(shè)備和環(huán)境自動選擇合適的列表樣式。
plain:普通列表樣式。
grouped:分組列表樣式。
insetGrouped:縮進(jìn)分組列表樣式。
sidebar:側(cè)邊欄列表樣式。
inset:縮進(jìn)列表樣式。
以上樣式的效果圖如下,有些樣式不太明顯,結(jié)合其他的修飾符效果可能更好。
分組顯示(Section、 Header、 Footer)
List組件要想實(shí)現(xiàn)分組的功能,很簡單,在List組件中使用Section組件即可。Section組件支持Header和Footer功能,同時Header和Footer也支持直接設(shè)置Title和自定義。
比如下面直接設(shè)置Section title的示例,直接給Title一個字符串,在content閉包內(nèi)通過ForEach循環(huán)添加要顯示的組件,當(dāng)然也可以不用循環(huán),而是靜態(tài)數(shù)據(jù)。
下面是自定義Header和Footer的示例,Header中橫向顯示了Image和Text組件,Footer中添加了一個Text,效果如下:
這里額外說一下List和ForEach循環(huán)的結(jié)合使用的場景:
如果只是單純的顯示一些靜態(tài)數(shù)據(jù),或者一個數(shù)據(jù)數(shù)據(jù),只用List組件即可。
如果要顯示數(shù)組數(shù)據(jù),可以單獨(dú)使用List組件,將數(shù)組數(shù)據(jù)傳給List,也可以在List組件中添加ForEach組件,通過ForEach循環(huán)遍歷數(shù)組數(shù)據(jù)。
var body: some View {List {ForEach(fruits, id: \.self) { fruit inText(fruit.capitalized)}}}
如果即有靜態(tài)數(shù)據(jù)又有動態(tài)數(shù)據(jù)要顯示,則需List和ForEach組合使用:
上面的代碼稍微有些多,主要為了視覺效果更好一些。從效果圖中可以看出是人為設(shè)置了兩個Header,采用了Text組件,并設(shè)置了相關(guān)的屬性修飾符。代碼中不難看出List組件里面采用了ForEach,同時也單獨(dú)設(shè)置了Text組件。
另外需要提一下,我們給ForEach設(shè)置了前景顏色,這個前景顏色會作用在ForEach中的每個Text上。還有給Group組件設(shè)置了List cell的樣式,那么這個樣式也會作用在List中的每個子組件上,這樣做代碼會更加簡潔明了。
添加和刪除元素
只能在List的動態(tài)部分添加和刪除元素,而靜態(tài)元素不能在應(yīng)用程序運(yùn)行時添加或刪除。因此,我們的列表需要完全動態(tài),或者至少包含一個動態(tài)部分,以便我們能夠添加或刪除元素。
為了有添加和刪除的入口,我們添加了導(dǎo)航欄,并設(shè)置了導(dǎo)航欄的toolbar,導(dǎo)航欄的設(shè)置這里不過多闡述。
添加元素
在導(dǎo)航欄的右側(cè)添加了一個添加元素的按鈕,點(diǎn)擊后在List要展示的數(shù)據(jù)中添加元素,此時也要求該數(shù)據(jù)必須被@State包裹修飾,以便其修改后觸發(fā)UI刷新。具體代碼見下面效果圖。
刪除元素
在導(dǎo)航欄左側(cè)添加了一個EditButton,點(diǎn)擊后將整個List設(shè)置為編輯模式,并在每個cell的左側(cè)提供一個紅色的刪除按鈕。這個過程主要是由系統(tǒng)自動的。不過前提是要對List添加至少一個可操作的方法,比如onDelete,onMove等,否則點(diǎn)擊了EditButton,List也沒有任何變化。
另外對單個cell進(jìn)行左滑操作也可以刪除元素。
為了實(shí)現(xiàn)刪除效果,需要在ForEach上添加onDelete方法,如下:
.onDelete(perform: { indexSet in})
這個onDelete方法返回一個IndexSet類型的值,我們可以在這個閉包內(nèi)進(jìn)行數(shù)據(jù)刪除,不過為了UI邏輯和業(yè)務(wù)邏輯低耦合,采用單獨(dú)抽出來一個方法處理刪除邏輯,如下:
func deleteItem(indexSet: IndexSet) {fruits.remove(atOffsets: indexSet)
}
在deleteItem方法里面,fruits數(shù)組調(diào)用remove(atOffsets:)方法即可。
因?yàn)閛nDelete方法返回一個IndexSet類型的值,所以抽出來的方法同樣接受這樣類型的值。最終只要在ForEach上調(diào)用下面代碼即可,不用額外寫傳遞的參數(shù):
ForEach(fruits, id: \.self) { fruit inText(fruit.capitalized)
}
.onDelete(perform: deleteItem)
最終代碼及效果如下圖:
移動功能
移動功能還是比較普遍的,基于上面的代碼,只需要在ForEach上添加下面的方法即可。
.onMove(perform: { indices, newOffset in})
這個方法返回兩個參數(shù),第一個為IndexSet類型的值,第二個為Int類型的值。同onDelete一樣,我們單獨(dú)抽出來一個方法,同樣接收這兩個參數(shù)。
func moveItem(indexSet: IndexSet, offSet: Int) {fruits.move(fromOffsets: indexSet, toOffset: offSet)
}
在moveItem方法中,fruits數(shù)據(jù)調(diào)用move(fromOffsets:, toOffset:)方法即可。最后在ForEach上添加該方法:
ForEach(fruits, id: \.self) { fruit inText(fruit.capitalized)
}
.onMove(perform: moveItem)
實(shí)現(xiàn)了上面的代碼后,我們可以在List的編輯模式下拖動cell移動,編輯模式下每個cell右側(cè)有三個橫杠的圖標(biāo),拖動即可移動。另外非編輯模式下長按cell后也可以進(jìn)行拖動,代碼及效果圖如下:
?
自定義左滑功能
SwiftUI的swipeActions()修飾符允許你添加一個或多個滑動動作按鈕到你的列表行,可選地控制他們屬于哪一邊,以及他們是否應(yīng)該被觸發(fā)使用一個完整的滑動。
先看下代碼及效果圖:?
swipeActions()方法有三個參數(shù),第一個edge決定操作按鈕放哪邊;第二個allowsFullSwipe決定完全滑動是否自動執(zhí)行第一個操作,默認(rèn)值為true;第三個即是內(nèi)容閉包了。
關(guān)于樣式,只能通過tint設(shè)置背景色,如果不設(shè)置,系統(tǒng)默認(rèn)是灰色的。
SwiftUI底層還是很聰明的,比如第一個Button,我們用Text顯示內(nèi)容,只有文字,那么系統(tǒng)就顯示了Add文字,第二個和第三個用Label顯示,有文字和圖片,但是系統(tǒng)聰明的只顯示了圖片。
對于想要擁有刪除功能的按鈕,應(yīng)該使用Button(role: .destructive),而不是僅僅指定一個紅色背景色,比如上面第三個按鈕,不需要有任何刪除的邏輯,聰明的底層幫我們實(shí)現(xiàn)了。
Button(role: .destructive) {} label: {Label("Delete", systemImage: "trash.fill")
}
SwiftUI中的List確實(shí)是一個非常好用的組件,包含的功能也比較多,大多數(shù)的App界面也都是由導(dǎo)航欄和List組成的。