2010-07-05 3 views
4

Donc, je suis totalement perplexe par celui-ci et tenté d'appeler "OS bug".UITableView plantage sur les mises à jour après l'édition de texte

J'ai un contrôleur TableView avec une section unique, et dans l'en-tête de cette section il y a un UITextField. Plusieurs opérations résultent en des lignes ajoutées/supprimées sans problème. Cependant, dès que le texte est modifié dans l'en-tête et que le clavier est fermé, toute insertion/suppression de lignes entraîne un plantage immédiat.

Et il peut être encore simplifié - appeler simplement beginUpdates/endUpdates sur la table une fois que le clavier est fermé est suffisant pour causer un plantage. La fin de la callstack est:

_CFTypeCollectionRetain 
_CFBasicHashAddValue 
CFDictionarySetValue 
-[UITableView(_UITableViewPrivate) _updateWithItems:withOldRowData:oldRowRange:newRowRange:context:] 
-[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] 
-[UITableView endUpdates] 

Je l'ai mis en place un exemple minimal qui illustre le problème.

source contrôleur complet: http://www.andrewgrant.org/public/TableViewFail.txt

Exemple de projet: http://www.andrewgrant.org/public/TableViewCrash.zip

Code le plus pertinent:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 
{ 
    // create header view 
    UIView* header = [[[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 320.f, 50.f)] autorelease]; 

    // text field 
    UITextField* textField = [[[UITextField alloc] initWithFrame:CGRectMake(10.f, 12.f, 300.f, 28.f)] autorelease]; 
    textField.text = @"Edit, then 'Save' will crash"; 
    textField.borderStyle = UITextBorderStyleRoundedRect; 
    textField.clearButtonMode = UITextFieldViewModeAlways; 
    textField.delegate = self; 

    [header addSubview:textField]; 

    return header; 
} 

- (BOOL)textFieldShouldReturn:(UITextField *)textField 
{ 
    // no purpose, but demonstrates updates work at this point 
    [self.tableView beginUpdates]; 
    [self.tableView endUpdates]; 

    [textField resignFirstResponder]; 

    // immediate crash 
    [self.tableView beginUpdates]; 
    [self.tableView endUpdates]; 
    return YES; 
} 
+0

À noter - l'accident se produit indépendamment du fait que le délégué de textField soit le contrôleur, ou une instance d'objet totalement indépendante. –

+0

J'ai regardé votre projet. Cela fonctionne très bien dans 3.x, mais comme vous l'avez dit, il se bloque sur 4.0 lorsque vous cliquez sur Enregistrer. +1 pour un bug difficile :) – Kalle

+1

Une chose qui vient à l'esprit est que votre fichier XIB a une vue tabulaire qui pointe vers RootViewController comme source de données et comme délégué, tandis que le contrôleur de vue racine instancie à son tour une instance de TableViewFail Haut". Cela ne devrait pas poser de problème, mais vous avez deux tables empilées les unes sur les autres. – Kalle

Répondre

1

Juste une mise à jour - j'ai présenté un rapport de bogue et cas repro à Apple et ils ont confirmé qu'il est un bug dans iOS 4.0. À partir de iOS 4.1 beta 2, il n'avait pas été corrigé.

Mon travail consistait à transformer la première ligne de ma table en pseudo en-tête qui occupe toute la vue du contenu et a une hauteur personnalisée. Ce n'est pas tout à fait aussi bon (les choses ne peuvent pas atteindre le bord de l'écran par exemple) mais c'est proche et ne plante pas.

+0

merde Je viens d'être brûlé par ça aussi. – TomSwift

+0

avez-vous le radar #? – TomSwift

0

Je suis tombé sur ce bug moi-même et je suis tellement content que j'ai trouvé votre message parce que je me cognais la tête sur mon bureau en essayant de comprendre où je foirais.

Pour ma solution de contournement, j'ai créé un UITextField hors écran et appelé becomeFirstResponder puis resignFirstResponder sur ce champ de texte avant de procéder aux mises à jour. Cela évitait le crash et ne nécessitait aucune refonte des en-têtes ou des cellules.

+0

J'ai essayé ceci et cela n'a pas fonctionné pour moi. Je pense que j'ai fait ce que vous avez décrit - créer un UITextField, que j'ai attaché à la fenêtre de l'application, mais alpha'd pour être caché. Appelé [tf becomeFirstResponder] suivi de [tf resignFirstResponder] - les deux réussissent (et le clavier s'éloigne). Mais je reçois toujours le plantage lorsque j'appelle [tableView endUpdates] quelques lignes plus tard. Avez-vous d'autres indices? – TomSwift

+0

Voici ce que j'ai fini par faire: '[hiddenField becomeFirstResponder]; [NSTimer scheduledTimerWithTimeInterval: 0 cible: auto-sélecteur: @selector (dismissKeyboard :) userInfo: nil répète: NO]; 'et simplement appelé resignFirstResponder dans dismissKeyboard \t – Cameron

1

J'ai frappé ce bug la nuit dernière et j'ai passé quelques heures ce matin à essayer de le comprendre. Les autres réponses dans ce fil n'ont pas fonctionné pour moi, mais m'ont aidé à trouver une solution de contournement que je pense est le meilleur.

Cameron a suggéré de créer un UITextField hors écran firstResponder, puis de le renvoyer avant d'appeler endUpdates sur la tableview. Cela n'a pas fonctionné pour moi, mais cela m'a donné une idée.

Dans le contexte de mon affichage d'en-tête personnalisé, je re-parent le champ de texte (dans mon cas, un UISearchBar, en fait) avant d'appeler resignFirstResponder. Alors je l'ai remis:

[self.window addSubview: sb]; 
[sb resignFirstResponder]; 
[self addSubview: sb]; 

quelques lignes plus tard, quand je l'appelle [endUpdates tableView], il ne se bloque plus.

Éditer: Cela devient un peu plus compliqué. Le problème est que si le statut du premier répondeur est autrement révoqué (par exemple, l'utilisateur rejette le clavier), ce code de permutation des parents n'est pas exécuté, et nous finirons par avoir le crash. Mon correctif actuel est de placer un remplacement de catégorie sur UITextField resignFirstResponder - semble fonctionner mais pas encore sûr s'il y a des effets secondaires indésirables.

@implementation UITextField (private) 

- (BOOL) resignFirstResponder 
{ 
    UIView* superviewSave = self.superview; 
    [self.window addSubview: self]; 
    BOOL success = [super resignFirstResponder]; 
    [superviewSave addSubview: self]; 
    return success; 
} 

@end 
0

Prendre la solution de Tom un peu plus loin, je remarquais cette solution ne fonctionne que sur iOS 4.X, ce qui est bien parce que ce problème existe seulement dans iOS 4.X Par conséquent j'ai changé sa méthode à:

@implementation customUITextField 

- (BOOL)resignFirstResponder { 

if ([[UIDevice currentDevice].systemVersion characterAtIndex:0] == '4') { 

    UIView* superviewSave = self.superview; 
    [self.window addSubview:self]; 
    BOOL success = [super resignFirstResponder]; 
    [superviewSave addSubview:self]; 
    return success; 

} 

return [super resignFirstResponder]; 

} 

@end