使用SwiftUI构建视频App2-列表页

2020-03-23  本文已影响0人  梦即是幻

列表页

image

如上图,列表页很简单,主要就是展示[VedioManager.File]数组,让我们看看怎么用Swift UI方式构建。

首先,创建代表列表页的VedioList:

import SwiftUI

struct VedioList {}

为什么是个空结构体?

因为,这里我们会用到Introducing Container views in SwiftUI这篇文章里面提到的思想,引入容器视图和渲染视图,这样做的好处建议大家详细看文章,最后总结大概如下:

这位作者也是介绍Swift相关教程,风格简洁一些,同样很屌很炸天。

容器视图应该只做与数据流相关的事情:

  1. 存储视图的状态
  2. 处理生命周期(onAppear / onDisappear)
  3. 使用ObservableObject获取数据
  4. 为“渲染”视图提供操作处理程序

渲染视图应该只执行与渲染相关的事情:

  1. 使用SwiftUI提供的原始组件构建用户界面。
  2. 使用其他渲染视图构建用户界面。
  3. 使用数据作为输入来呈现用户界面,不存储任何状态。

Content View

先来看看渲染视图,主要工作如下:

按照上面的思想,应该是这样:

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,代码量真的很少,很少,也比较简单。

需要注意的是,当我们点击一个cell的时候,会根据是否是目录,导航到不同界面,这里用到了AnyView。

不过根据文章How to return different view types,还可以使用Group。使用AnyView会降低性能,建议不要经常使用。

Container View

Container视图的主要工作就是

代码也很简单,而且当标记为@State的属性files发生变化时,Content View会自动更新,非常方便!

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)
                }
            }
        }
    }
}

这里有个坑,注意loadData方法里面的self.files = files这句代码,如果不放到DispatchQueue.main.async里,

当点击目录push到新的VedioLis.Containert时,会报table view Invalid update crash。。

上一篇 下一篇

猜你喜欢

热点阅读