从 UIDocument 获取单元格数据时,如何确保 UITableView 中显示的列表顺序

How to ensure the order of list shown in UITableView when getting data for cell from UIDocument

我的应用程序通过 FileManager 为 UITableView 中的每个单元格获取数据。单元格需要来自需要打开 UIDocument 对象的文件的数据。但是,打开完成处理程序中的代码似乎无法预测地执行,因此单元格不会按照我希望的顺序显示。

如何解决这个问题?如果有人提供任何线索,我将不胜感激。

override func viewWillAppear(_ animated: Bool) {
   super.viewWillAppear(true)
   fetchCellsData()
}

func fetchCellsData() {

    do {
        let files = getUrlsOfFiles()    //files in a certain order
        for file in files {

            let document = MyDocument(fileURL: file) //MyDocument subclassing UIDocument

            //open to get data for cell
            document.open { (success) in
                if !success {
                    print("File not open at %@", file)
                    return
                }

                let data = populateCellData(document.fileInfo)

                self.cellsData.append(data)
                self.tableView.reloadData()

                /// tried below also
                //  DispatchQueue.main.async {
                //    self.cellsData.append(data)
                //    self.tableView.reloadData()
                //  }
            }
            document.close(completionHandler: nil)
        }

    } catch {
        print(error)
    }
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! MyCell
    let fileInfo = cellsData[indexPath.row].fileInfo
    //assign fileInfo into MyCell property
    return cell
}

我希望单元格按照 'files' 的顺序呈现,但是,看起来顺序有点不可预测,假设这是由于 'cellForRowAt' 在它知道之前被调用的事实全套 'cellsData'.

来自 UIdocument 上的 Apple 文档。 Apple doc:

open(completionHandler:)

Opens a document asynchronously.

这意味着即使您以正确的顺序触发 document.open,也无法保证 completionHandler 序列的顺序相同,这就是顺序为 的原因不可预测.

但是,您知道他们会最终全部完成。

你可以做的是:

1 - 将打开文档中的所有数据放入另一个列表

2 - order这个列表根据你的需要

3 - 将此列表放入 cellsData(我假设它绑定到您的 tableViesDatasource)


var allDatas: [/*your data type*/] = []

...

    do {
        // reset allDatas 
        allDatas = []
        let files = getUrlsOfFiles()

        for file in files {

            let document = MyDocument(fileURL: file) 
            document.open { (success) in
                if !success {
                    print("File not open at %@", file)
                    return
                }

                let data = populateCellData(document.fileInfo)

                self.allDatas.append(data) // "buffer" list
                self.tableView.reloadData()  
            }
            document.close(completionHandler: nil)
        }

    } catch { ... 

然后您将 cellsData 更改为计算的 属性,如下所示:

var cellsData: [/*your data type*/] {
   get {
      return allDatas.order( /* order in accordance to your needs */ )
   }
}

这样,每次添加新数据时,列表在重新显示之前都是有序的。


讨论

这是关于代码状态的更简单的解决方案,但这可能不是总体首选解决方案。例如,在您的代码中,您每次 添加一个新值时 都重新加载您的表视图,因为您知道之后会添加更多数据,而这并没有得到优化。

我建议你阅读 Dispatch Group,这是一种等到你触发的所有异步操作完成后再执行某些操作(例如在此重新加载你的表视图)的方法案例)(读数:Raywenderlich tuts