列表頁
如上圖滑沧,列表頁很簡單俩滥,主要就是展示[VedioManager.File]數(shù)組,讓我們看看怎么用Swift UI方式構(gòu)建荚醒。
首先芋类,創(chuàng)建代表列表頁的VedioList:
import SwiftUI
struct VedioList {}
為什么是個(gè)空結(jié)構(gòu)體?
因?yàn)榻绺螅@里我們會(huì)用到Introducing Container views in SwiftUI這篇文章里面提到的思想侯繁,引入容器視圖和渲染視圖,這樣做的好處建議大家詳細(xì)看文章铺董,最后總結(jié)大概如下:
這位作者也是介紹Swift相關(guān)教程巫击,風(fēng)格簡潔一些禀晓,同樣很屌很炸天。
容器視圖應(yīng)該只做與數(shù)據(jù)流相關(guān)的事情:
- 存儲(chǔ)視圖的狀態(tài)
- 處理生命周期(onAppear / onDisappear)
- 使用ObservableObject獲取數(shù)據(jù)
- 為“渲染”視圖提供操作處理程序
渲染視圖應(yīng)該只執(zhí)行與渲染相關(guān)的事情:
- 使用SwiftUI提供的原始組件構(gòu)建用戶界面坝锰。
- 使用其他渲染視圖構(gòu)建用戶界面粹懒。
- 使用數(shù)據(jù)作為輸入來呈現(xiàn)用戶界面,不存儲(chǔ)任何狀態(tài)顷级。
Content View
先來看看渲染視圖凫乖,主要工作如下:
- 根據(jù)傳入的File數(shù)組創(chuàng)建cell
- 處理左滑刪除的響應(yīng)
按照上面的思想,應(yīng)該是這樣:
extension VedioList {
struct Content: View {
var files: [VedioManager.File]
var delete: (_ offsets: IndexSet) -> Void
var body: some View {
List {
ForEach(files, id: \.self) { file in
self.cell(for: file)
}
.onDelete(perform: delete)
}
}
private func cell(for file: VedioManager.File) -> AnyView {
file.isFolder ?
NavigationLink(file.name, destination: Container(path: file.path)).eraseToAnyView() :
NavigationLink(file.name, destination:VedioPlayer.Container(file: file)).eraseToAnyView()
}
}
}
可以看到弓颈,用Swift UI帽芽,代碼量真的很少,很少翔冀,也比較簡單导街。
需要注意的是,當(dāng)我們點(diǎn)擊一個(gè)cell的時(shí)候纤子,會(huì)根據(jù)是否是目錄搬瑰,導(dǎo)航到不同界面,這里用到了AnyView控硼。
不過根據(jù)文章How to return different view types泽论,還可以使用Group。使用AnyView會(huì)降低性能卡乾,建議不要經(jīng)常使用翼悴。
Container View
Container視圖的主要工作就是
- onAppear時(shí)加載并更新指定目錄的數(shù)據(jù)
- 處理刪除邏輯
代碼也很簡單,而且當(dāng)標(biāo)記為@State的屬性files發(fā)生變化時(shí)幔妨,Content View會(huì)自動(dòng)更新鹦赎,非常方便!
extension VedioList {
struct Container: View {
let path: String
@State
private var files: [VedioManager.File] = []
var body: some View {
Content(files: files, delete: delete)
.navigationBarTitle(path.lastPathComponent)
.onAppear(perform: loadData)
}
private func loadData() {
print(path)
let files = VedioManager.load(at: path)
DispatchQueue.main.async {
self.files = files
}
}
private func delete(at offsets: IndexSet) {
for idx in offsets {
let file = files[idx]
if VedioManager.delete(file) {
files.remove(at: idx)
}
}
}
}
}
這里有個(gè)坑陶冷,注意loadData方法里面的self.files = files這句代碼钙姊,如果不放到DispatchQueue.main.async里毯辅,
當(dāng)點(diǎn)擊目錄push到新的VedioLis.Containert時(shí)埂伦,會(huì)報(bào)table view Invalid update crash。思恐。