从CloudKit

时间:2017-02-17 13:17:37

标签: ios uitableview swift3 cloudkit

我目前正在从CloudKit检索数据并成功填充表格视图。

我有一个实例检测滚动是否在最后一行,如果是,则重新排列服务器以检索更多数据。

成功检索到所需数据后,我在特定的indexPath处插入特定项,然后重新加载表视图。

当我在最后一行时,tableView.reloadData()使我的应用程序闪烁屏幕,但一切正常。

几秒钟后,我的调试开始为每个单元格打印相同的错误:

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.

我正在计算heightForRowAtIndexPath:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    var cell = PostFeedTableViewCell()
    cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithImage) as! PostFeedTableViewCell
    return cell.bounds.size.height;
}

我的代码在用户到达最后一行后检索数据如下:

func continueQuering(cursor: CKQueryCursor?) {
    if cursor != nil {
        self._loadingData = true

        let publicData = CKContainer.default().publicCloudDatabase
        let predicate = NSPredicate(format: "TRUEPREDICATE", argumentArray: nil)

        let q = CKQuery(recordType: "Posts", predicate: predicate)
        q.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

        let qop = CKQueryOperation(query: q)
        qop.resultsLimit = 5
        qop.cursor = cursor

        qop.recordFetchedBlock = {(record) in
            print("appending")
            self.posts.append(record)
            self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
        }

        qop.queryCompletionBlock  = { [weak self] (cursor : CKQueryCursor?, error : Error?) -> Void in
            print("********************")
            print("cursor = \(String(describing: cursor)) , error = \(String(describing: error))")
            print("********************")
            self?._Cursor = cursor 
            self?._loadingData = false
            DispatchQueue.main.async {
                print("reloading async ******")
                self?.tableView.reloadData()
            }
        }
        publicData.add(qop)
    }
}

我需要一些关于如何避免闪烁屏幕的帮助,可能是由tableView.reloadData()函数引起的,以及如何修复几秒钟后提示的这些警告。

如果我没有调用reloadData()方法,则添加所有行但滚动不移动,卡在前一行;

感谢。

2 个答案:

答案 0 :(得分:1)

所以我设法解决了这个问题,我将解释如何然后提供更新的代码;

当我使用后台线程从服务器检索代码时,我遇到了更新UI的问题,因为Apple文档说我们应该仅在主线程中更新UI。

实施

DispatchQueue.Main.async {
    self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
}

我仍然遇到问题,应用程序崩溃时提示错误提醒应用程序数据源在更新前后应具有相同的对象。

我弄清楚在后台线程上附加数据然后同时更新主线程上的UI会触发该错误,即使这样,我的tableView滚动没有更新,如果我之前有5行,之后插入,比方说10行,滚动仍然会卡在第5行。

要解决这个问题,我必须与tableView.beginUpdates() | tableView.endUpdates()属性同步进行更新,因此tableView会在dataSource数组填充的同时更新,并使用begin / end updates更新它自己的属性而不是调用reloadData()来更新整个tableView。

我注意到配置要在willDisplayCell()方法上显示的单元格,使应用程序略有错误,不是非常流畅,所以我已将配置移至cellForRowAtIndexPath()方法,并且仅检查willDisplayCell()方法是否显示最后一个方法,如果是,则查询服务器以从前一个光标点开始检索更多数据。

如上所述,解析代码如下:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // Default não tem image, assim se não houver posts para download retorna a cell vazia
    var cell = PostFeedTableViewCell()

    if (posts.count == 0) { return cell } /* Não tem posts, retorna uma cell vazia */

    let post = posts[indexPath.row]

    // Instancia o reuse identifier
    if post["post_image"] != nil {
        cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithImage, for: indexPath) as! PostFeedTableViewCell
    } else {
        cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithoutImage, for: indexPath) as! PostFeedTableViewCell
    }
    configureCell(cell: cell, atIndexPath: indexPath)
    return cell
}


override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if (!_loadingData) && indexPath.row == posts.count - 1 {
        print("last row displayed")
        continueQuering(cursor: _Cursor)
    }
}

func continueQuering(cursor: CKQueryCursor?) {
    if cursor != nil {
        self._loadingData = true

        // novo query com cursor a continuar na posição anterior
        let publicData = CKContainer.default().publicCloudDatabase
        let predicate = NSPredicate(format: "TRUEPREDICATE", argumentArray: nil)

        let q = CKQuery(recordType: "Posts", predicate: predicate)
        q.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

        let qop = CKQueryOperation(query: q)
        qop.resultsLimit = 5
        qop.cursor = cursor

        qop.recordFetchedBlock = {(record) in
            self.posts.append(record)
            print("appending...")
            DispatchQueue.main.sync {
                print("updating...")
                self.tableView.beginUpdates()
                self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
                self.tableView.endUpdates()
            }

        }

        qop.queryCompletionBlock  = { [weak self] (cursor : CKQueryCursor?, error : Error?) -> Void in
            self?._Cursor = cursor // Actualiza o cursor se houver mais dados
            self?._loadingData = false
            print("cursor = \(cursor)")
        }
        publicData.add(qop)

    }
}

答案 1 :(得分:0)

我认为错误源于后台线程中完成块中运行的libraryDependencies ++= Seq("org.apache.spark" %% "spark-core" % "2.0.0" % "provided", "org.apache.spark" %% "spark-mllib" % "2.0.0") 。尝试将self.tableView.insertRows发送到主线程:

insertRows