从 CloudKit 检索新数据后更新 tableView 数据
Update tableView data after retrieving new data from CloudKit
我目前正在从 CloudKit 检索数据并成功填充 table 视图。
我有一个实例可以检测滚动是否在最后一行,如果是,它会重新查询服务器以检索更多数据。
成功检索到所需数据后,我在特定的 indexPath 处插入特定的项目,然后重新加载 table 视图。
当我在最后一行时,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() 方法,所有的行都会被添加,但滚动条不会移动,卡在前一个最后一行;
谢谢。
我认为错误源于后台线程中完成块中的 self.tableView.insertRows
运行。尝试调度 insertRows
到主线程:
qop.recordFetchedBlock = {(record) in
print("appending")
self.posts.append(record)
DispatchQueue.main.async {
self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
}
}
所以我设法解决了这个问题,我将解释如何解决,然后提供更新的代码;
当我使用后台线程从服务器检索代码时,我在更新 UI 时遇到了问题,因为 Apple 文档说我们应该只在主线程中更新 UI。
实施
之后
DispatchQueue.Main.async {
self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
}
我还是遇到了问题,应用崩溃提示错误提醒应用数据源应该在更新前后具有相同的对象。
我发现在后台线程上附加数据然后同时在主线程上更新 UI 会引发该错误,即使如此,如果我之前有 5 行,插入后,比如说 10 行,滚动仍然会卡在第 5 行。
为了解决这个问题,我必须与 tableView.beginUpdates() | tableView.endUpdates()
属性同步进行更新,因此 tableView 会在填充 dataSource 数组的同时更新,并使用 [= 更新它自己的属性33=] 更新,而不是调用 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)
}
}
我目前正在从 CloudKit 检索数据并成功填充 table 视图。
我有一个实例可以检测滚动是否在最后一行,如果是,它会重新查询服务器以检索更多数据。
成功检索到所需数据后,我在特定的 indexPath 处插入特定的项目,然后重新加载 table 视图。
当我在最后一行时,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() 方法,所有的行都会被添加,但滚动条不会移动,卡在前一个最后一行;
谢谢。
我认为错误源于后台线程中完成块中的 self.tableView.insertRows
运行。尝试调度 insertRows
到主线程:
qop.recordFetchedBlock = {(record) in
print("appending")
self.posts.append(record)
DispatchQueue.main.async {
self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
}
}
所以我设法解决了这个问题,我将解释如何解决,然后提供更新的代码;
当我使用后台线程从服务器检索代码时,我在更新 UI 时遇到了问题,因为 Apple 文档说我们应该只在主线程中更新 UI。
实施
之后DispatchQueue.Main.async {
self.tableView.insertRows(at: [IndexPath.init(row: self.posts.count - 1, section: 0)], with: .automatic)
}
我还是遇到了问题,应用崩溃提示错误提醒应用数据源应该在更新前后具有相同的对象。
我发现在后台线程上附加数据然后同时在主线程上更新 UI 会引发该错误,即使如此,如果我之前有 5 行,插入后,比如说 10 行,滚动仍然会卡在第 5 行。
为了解决这个问题,我必须与 tableView.beginUpdates() | tableView.endUpdates()
属性同步进行更新,因此 tableView 会在填充 dataSource 数组的同时更新,并使用 [= 更新它自己的属性33=] 更新,而不是调用 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)
}
}