2017-02-17 4 views
0

Ich lade derzeit Daten aus CloudKit ab und bearbeite die Tabellenansicht erfolgreich.Tabelle aktualisierenDaten anzeigen, nachdem neue Daten aus CloudKit abgerufen wurden

Ich habe eine Instanz, die erkennt, ob der Bildlauf in der letzten Zeile ist, und wenn dies der Fall ist, wird der Server neu in die Warteschlange gestellt, um weitere Daten abzurufen.

Nach dem Abrufen der gewünschten Daten, ich füge die spezifischen Elemente, bei bestimmten IndexPath, und dann lade ich die Tabellenansicht.

Was passiert ist, dass, wenn ich in der letzten Zeile bin, die tableView.reloadData() meine App den Bildschirm flackert, aber alles funktioniert einwandfrei.

Nach wenigen Sekunden beginnt mein Debug den gleichen Fehler drucken, für jede Zelle, die ist:

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.

ich die heightForRowAtIndexPath wie diese Berechnung:

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

Und mein Code zum Abrufen von Daten, sobald der Benutzer die letzte Zeile erreicht, ist folgender:

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

Ich brauche Hilfe, um den Flimmerbildschirm zu vermeiden, wahrscheinlich verursacht durch die Funktion tableView.ReloadData(), und wie man diese Warnungen korrigiert, die nach ein paar Sekunden auftauchen.

Wenn ich die reloadData() -Methode nicht aufrufen, werden alle Zeilen hinzugefügt, aber der Bildlauf bewegt sich nicht, bleibt in der letzten Zeile hängen;

Danke.

+0

Wäre es eine gute Option sein, durch die neuen Beiträge zu durchlaufen und auf dieser Iteration sie im Tableview einfügen und dann die Tableview Daten neu zu laden, alle asynchron ? –

Antwort

1

So habe ich es geschafft, dieses Problem zu lösen, werde ich erklären, wie und geben Sie dann den aktualisierten Code;

Da ich den Code vom Server mit einem Hintergrundthread abrufen, hatte ich Probleme beim Aktualisieren der Benutzeroberfläche, da Apple-Dokumentation sagt, dass wir die Benutzeroberfläche nur im Hauptthread aktualisieren sollten.

Nach dem

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

Umsetzung hatte ich noch Probleme, und die App abgestürzt den Fehler Aufforderung daran erinnert, dass die App-Datenquelle die gleichen Objekte vor und nach dem Update haben sollte.

Ich finde heraus, dass das Anhängen von Daten auf einem Hintergrund-Thread und dann die Aktualisierung der Benutzeroberfläche auf dem Haupt-Thread gleichzeitig diesen Fehler auslösen würde, und obwohl das mein TableView-Scroll nicht aktualisiert, wenn ich zuvor 5 Zeilen hatte, Nach dem Einfügen, sagen wir 10 Zeilen, würde die Schriftrolle immer noch auf der 5. hängen.

Um das Problem zu lösen, musste ich das Update synchron mit den Eigenschaften tableView.beginUpdates() | tableView.endUpdates() durchführen, so dass das TableView zur gleichen Zeit aktualisiert würde wie das dataSource-Array und stattdessen seine eigenen Eigenschaften mit den begin/end-Updates aktualisiert würde Rufen Sie die reloadData() auf, um das gesamte TableView zu aktualisieren.

Ich habe bemerkt, dass die Zelle die Konfiguration auf dem willDisplayCell() Methode zu präsentieren, die App leicht Buggy, soweit sie nicht vollkommen glatt, also habe ich die Konfiguration in das cellForRowAtIndexPath() Verfahren bewegt, und nur in der willDisplayCell() Methode überprüft, ob es die letzte zu präsentieren, und wenn dies der Fall ist, dann fragen Sie den Server ab, um mehr Daten abzurufen, beginnend am vorherigen Cursorpunkt.

Wie gesagt, das Auflösungs Code ist folgende:

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

Froh, dass es funktioniert hat! Ein anderer Gedanke, siehst du einen sichtbaren Unterschied, wenn du den Zeileneinsatz komplett entfernst und nur die Tabelle am Ende mit 'reloadData' änderst? – Thunk

+0

Ich sage, ich habe bereits 50 Elemente (Zeilen) in der Tabellenansicht und ich füge 5 zu ihnen hinzu. Wenn ich die gesamten Daten neu lade, sehe ich ein Flash-Flackern des Bildschirms, weil es die gesamten Datenelemente aktualisiert, und wenn ich sie 1-mal-1 mit den Start- und Endeaktualisierungen einfüge, wird nur für die hinzugefügte Zeile und aktualisiert optisch sieht es normal aus, ohne dass der Bildschirm neu geladen wird. Wenn ich alle Elemente ersetzen würde, wäre es toll, 'reloadData' aufzurufen, aber da ich nicht bin, erledigt insertRowAtIndex die Aufgabe. –

+0

Ich verstehe. In jedem Fall ist das eine gründliche, gut geschriebene Erklärung darüber, wie Sie die asynchronen Aufrufe erfolgreich gewürgt haben! – Thunk

0

Ich glaube, der Fehler stammt von self.tableView.insertRows im Completion-Block in einem Hintergrund-Thread ausgeführt wird. Versuchen Sie insertRows zu den Haupt-Thread Dispatching:

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

Ich werde es versuchen und ich werde gleich antworten. Ich habe etwas ähnliches versucht, aber nachdem alle neuen Zeilen eingefügt wurden, erlaubte mir die Tabellenansicht nicht, zu ihnen zu blättern, blieb in der vorherigen Tabellenansicht "innere Höhe" hängen. Ich werde dir bald Feedback geben. Danke –

Verwandte Themen