wake-up-neo.com

Was bedeutet die Meldung "Kein Indexpfad für wiederverwendete Tabellenzellen" in iOS 6/7?

Seit dem Kompilieren meiner App mit iOS 6 (und seit iOS 7) habe ich diese Nachricht angezeigt. Ich weiß, dass sich UITableViews in iOS 6 anders verhält, aber ich musste meinen Code nicht ändern, damit er weiterarbeiten kann. Aber ich befürchte, diese Nachricht könnte auf ein mögliches Problem hinweisen, das ich noch nicht gesehen habe ... Kann jemand Licht werfen?

57
Nathan Brown

Ich habe diese Fehlermeldung ab iOS 7 Beta 5 im Protokoll angezeigt, auch im iOS 7 GM/Release-Build. In iOS 6 oder früheren iOS 7-Betas war dies noch nie in meiner App der Fall. Nach langem Experimentieren fand ich die Ursache:

Ich habe UITableViewCell-Objekte für die Ansichten der Kopfzeile des Abschnitts verwendet und sie in tableView:viewForHeaderInSection: zurückgegeben. Dies scheint allgemein üblich zu sein, vor allem seit iOS 5, als es leicht wurde, die Ansicht einer Abschnittsheader als Prototyp einer Tabellenzelle in einem StoryBoard mit Interface Builder zu entwerfen.

Als ich meine App so geändert hatte, dass nur reguläre UIView-Unterklassen für die Kopfzeilenansichten der Abschnittsheader verwendet wurden, gingen die Fehler weg und vor allem löschte meine Tabellensicht das zufällige Löschen der Abschnittsheader!

Es scheint, dass UITableView (seit iOS 7 Beta 5) intern eine Zuordnung aller UITableViewCell-Objekte in ihrer Ansichtshierarchie und ihrer jeweiligen Indexpfade verwaltet. Da ein Abschnittsheader (oder ein Tabellensichtheader der Fußzeile) keinen Indexpfad hat, wenn Sie ein UITableViewCell-Objekt für diese Ansichten verwenden, wird die Tabellensicht verwirrt, wenn sie eine UITableViewCell findet, für die sie kein hat Indexpfad, wodurch der Fehler "Kein Indexpfad für Tabellenzelle wiederverwendet" wird. Wenn Sie Pech haben, zeigen Sie in Ihrer Tabellensicht Störungen an:

UPDATE: Wenn Sie Zugriff auf die Apple Dev-Foren haben, finden Sie hier den Thread (den ich gestartet habe): https://devforums.Apple.com/message/882042#882042

Wie in diesem Thread vorgeschlagen, können Sie einen UIView-Wrapper um Ihre UITableViewCell erstellen, wenn Sie nicht viel neu berechnen möchten, und diesen als Abschnittsheaderansicht zurückgeben. 

UIView *view = [[UIView alloc] initWithFrame:[cell frame]];
[view addSubview:cell];

return view;

Beachten Sie jedoch, dass dieser "Wrapper" -Ansatz UIView mit AutoLayout und Gerätedrehung nicht gut funktioniert. Ich empfehle Ihnen daher, eine UIView-Unterklasse für Kopf- und Fußzellen zu verwenden, keine UITableViewCell-Unterklasse, wie im Hauptteil der Antwort beschrieben.

132
mluisbrown

Ich würde die contentView der UITableViewCell zurückgeben, anstatt einen Wrapper zu erstellen

return cell.contentView;
41
mar-schmidt

Ich hatte das gleiche Problem und es dauerte einige Stunden, um das Problem zu lösen. Es stellte sich heraus, dass ich [textField becomeFirstResponder] beim Einrichten von Zellen aufgerufen hatte (hier war das textField Teil einer benutzerdefinierten tableviewcell). [textField becomeFirstResponder]in aktiviert die Mitteilung keyboardWillShow, wodurch sich die Tabellenansicht vorzeitig selbst lädt, wodurch der berüchtigte " kein Indexpfad für die Tabellenzelle wiederverwendet " angezeigt wird. Nachdem ich diesen Aufruf entfernt hatte, verschwand das Problem.

26
mpprdev

Neben der akzeptierten Antwort (mluisbrown) musste ich der Kopfzelle eine autoresizingMask hinzufügen, da meine eine mehrzeilige Markierung enthielt, d. H.

UIView *view = [[UIView alloc] initWithFrame:[cell frame]];
cell.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[view addSubview:cell];
return view;
16
Rob

Dies ist ein interner UIKit-Fehler - wie in den eigenen Entwicklerforen von Apple erwähnt. In neueren Versionen von xcode wurde es angeblich behoben, obwohl ich keine Informationen dazu finden konnte, welche Version dies behebt.

8
diegoreymendez

Als Ergänzung zu meinem vorherigen Beitrag (in dem ich erwähnte, dass dies anscheinend ein Fehler mit UIKit war), konnte ich eine Problemumgehung für meinen speziellen Fall finden (in dem die Nachricht mit seltsamen Visualisierungsstörungen auf dem Tisch zusammenhängt).

Anscheinend brauchte meine benutzerdefinierte Zelle -(void)setEditing:animated: zu lange, um zurückzukehren.

Mein vorheriger Code war:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{   
    [super setEditing:editing animated:animated];
    [self someAdditionalCode];
}

Ich konnte es reparieren, indem ich es in Folgendes änderte:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{   
    [super setEditing:editing animated:animated];

    // DRM: we want to perform the actions from this block in the main thread, but
    // asynchronously to avoid excessive delays which were causing issues.
    //
    dispatch_async(dispatch_get_main_queue(), ^void()
    {
        [self someAdditionalCode];
    });
}
4
diegoreymendez

Meine Endupdates nach resignfirstresponder haben mein Problem gelöst (Habe ein UITextFIeld in meiner benutzerdefinierten Zelle)

-(void)textfieldEditDone
{
....

    [textField resignFirstResponder];
    [self.tableView endUpdates];
2
Daniel Åkesson

Ich hatte das gleiche Problem mit der Fehlermeldung. Soweit ich sehen kann, wird dies durch das Neuladen der Tabellensicht aus einer Funktion verursacht, die vom Textfeld als Teil seines Delegatenprotokolls aufgerufen wird. Dh textFieldDidEndEditing -> [controller.tableview reload ...]

2
Dan

Für das Protokoll bin ich auch unter iOS 6 auf diese Nachricht gestoßen.

(NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section {
    NSInteger rows = 0;
    if ([delegate respondsToSelector:@selector(numberOfItemsInSection:)]) {
        rows = [delegate numberOfItemsInSection:section];

        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

Wenn die beginUpdate:/endUpdate: -Sequenz entfernt wurde, ist das Problem magisch verschwunden.

1
Fred The Bishop

Dies ist offensichtlich eine alte Frage, aber hoffentlich kann dies jedem helfen, der dieses Problem immer noch in iOS8 + hat, da dies immer noch die oberste Frage ist, die für diese bestimmte Fehlermeldung angezeigt wird.

Ich habe PINRemoteImage verwendet, um ein Bild asynchron zu einer UIImageView herunterzuladen, die sich in einer benutzerdefinierten UITableViewCell befand.

Um die Zeile richtig zu skalieren (dynamische Höhenzellen mit automatischem Layout), nachdem das Bild geladen wurde, rief ich Folgendes an:

self.tableView beginUpdates;
self.tableView endUpdates;

Ich erhielt dann die Meldung "Kein Indexpfad für wiederverwendete Tabellenzellen" und die App stürzte ab. Ich hatte geglaubt, der PINRemoteImageManagerResult-Block befände sich im Hauptthread. Es stellte sich jedoch heraus, dass dies nicht der Fall war. Daher wurde sichergestellt, dass die Start-/End-Updates für den Hauptthread aufgerufen wurden.

dispatch_async(dispatch_get_main_queue(), ^(void){
                            [self.tableView beginUpdates];
                            [self.tableView endUpdates];
});
1
siburb

Vielleicht hilft dies jemandem: Ich hatte einmal diesen Fehler beim Aktualisieren einer einzelnen Tabellenzelle. Ich wollte sowas machen 

NSIndexPath *reloadRow = [NSIndexPath indexPathForRow:1 inSection:2];
[self._mainTableView reloadRowsAtIndexPaths:@[reloadWebViewRow]
                           withRowAnimation:UITableViewRowAnimationFade];

Aber aus Versehen habe ich getippt

NSIndexPath *reloadRow = [NSIndexPath indexPathForItem:1 inSection:2];

Beachten Sie den Unterschied der beiden Indexpfade: Einer wird mit indexPathForItem (falsch) erstellt, der andere mit indexPathForRow (richtig). Dies alles führte zu einem sehr seltsamen Verhalten der tableView und der Fehlermeldung in der Überschrift. 

0
Tobias

Es scheint, dass mein Problem ausgelöst wurde, als ich versuchte, die Benutzeroberfläche innerhalb eines Teils des Codes zu aktualisieren, der ein Rückruf von einem Webanruf war. Ich habe das Problem behoben, indem UI-Aktualisierungen im Hauptthread erzwungen wurden. Ich habe Code so benutzt.

void runOnMainQueueWithoutDeadlocking(void (^block)(void)){
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

Ich nenne es innerhalb des Erfolgsblocks meines Hintergrund-Webanrufs wie folgt.

runOnMainQueueWithoutDeadlocking(^{
    [self.tableView beginUpdates];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
});
0

Noch eine andere Bedingung ...

Dies geschah, als ich nil zurückgab, weil ich keinen Header wollte.

Fix:

func tableView(tableView: UITableView,
               titleForHeaderInSection section: Int) -> String? {
    return ""
}
0
SwiftArchitect

Nun, ich habe gerade den größten Teil eines Tages ausprobiert, um das herauszufinden, also hoffentlich erspart diese alternative Erklärung jemandem etwas Zeit.

Ich hatte eine Tableview, die diese Nachricht manchmal gab, wenn geladen wurde. Es stellte sich heraus, dass KVO-Benachrichtigungen für Kerndatenobjekte beim Laden der Ansicht ausgelöst wurden. (Bei der Beobachtung einer Änderung hat mein Controller versucht, in der betreffenden Tabellenansicht den Namen reloadData zu verwenden. Dieses Problem wurde behoben, indem die Objekte nicht beobachtet wurden, bis die Ansicht vollständig geladen war (zuvor habe ich das Objekt beobachtet, nachdem es über den Accessor zugewiesen wurde)

TLDR: Überprüfen Sie, ob Sie möglicherweise versuchen, Ihre Daten von einem anderen als dem Haupt-Thread aus neu zu laden.

0
Jonathan Zhan