2017-02-17 23 views
0

В настоящее время я извлекаю данные из 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(), все строки добавляются, но свиток не перемещается, застревает в предыдущей последней строке;

Спасибо.

+0

Будет ли это хороший вариант, чтобы перебирать новые должности и на этой итерации вставить их в Tableview, а затем перезагрузить данные Tableview, все асинхронно ? –

ответ

1

Так что мне удалось решить эту проблему, я объясню, как и затем предоставить обновленный код;

Когда я извлекал код с сервера с фоновым потоком, у меня возникали проблемы с обновлением пользовательского интерфейса, так как в документации Apple говорится, что мы должны обновлять интерфейс только в основном потоке.

После реализации

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

Я до сих пор были проблемы, и приложение разбился вызвало ошибку, напоминающую, что источник данных приложения должны иметь одни и те же объекты до и после обновления.

Я понимаю, что добавление данных в фоновый поток, а затем обновление пользовательского интерфейса в главном потоке одновременно приведет к этой ошибке, и даже несмотря на то, что мой прокрутка таблицы не обновлялся, если раньше у меня было 5 строк, после вставки, скажем, 10 строк, свиток все равно застрял бы на 5-м.

Чтобы решить эту проблему, я должен был выполнить обновление синхронно с объектами tableView.beginUpdates() | tableView.endUpdates(), поэтому tableView будет обновляться одновременно с заполнением массивом dataSource и обновлением его собственных свойств с помощью обновлений begin/end, а не вызывая 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) 

    } 
} 
+0

Рад, что у вас это работает! Другая мысль, видите ли вы видимую разницу, если вы полностью удаляете вставку строк и только меняете таблицу в конце с помощью 'reloadData'? – Thunk

+0

Я, скажем, у меня уже 50 элементов (строк) в виде таблицы, и я добавляю к ним 5. Если я перезагружу все данные, у меня появится мерцание вспышки на экране, потому что оно обновляет все элементы данных, и если я вставляю их 1 на 1 с помощью begin и endUpdates, он обновляется только для добавленной строки и визуально выглядит нормально, без перезагрузки экрана. Если бы я заменил все элементы, было бы здорово называть 'reloadData', но так как я не, insertRowAtIndex выполняет эту работу. –

+0

Я вижу. В любом случае, это подробное, хорошо написанное объяснение того, как вы успешно нарушили асинхронные звонки! – Thunk

0

Я считаю, что ошибка связана с 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) 
     } 
} 
+0

Я попробую, и я отвечу в одно мгновение. Я пробовал что-то подобное, но после того, как все новые строки были вставлены, представление таблицы не позволяло мне прокручивать их, застрял в предыдущем представлении таблицы «внутренняя высота». Я скоро свяжусь с вами. Спасибо –