web-dev-qa-db-fra.com

Problème UITableView lors de l'utilisation de délégué / dataSource séparé

Description générale:

Pour commencer avec ce qui fonctionne, j'ai un UITableView qui a été placé sur une vue générée par Xcode en utilisant Interface Builder. Le propriétaire du fichier de la vue est défini sur une sous-classe générée par Xcode de UIViewController. À cette sous-classe, j'ai ajouté des implémentations de travail de numberOfSectionsInTableView: tableView:numberOfRowsInSection: et tableView:cellForRowAtIndexPath: et dataSource et delegate de la vue Table sont connectés à cette classe via le propriétaire du fichier dans Interface Builder.

La configuration ci-dessus fonctionne sans problème. Le problème se produit lorsque je souhaite déplacer les implémentations dataSource et delegate- de cette vue tabulaire vers une classe distincte, probablement parce qu'il existe d'autres contrôles sur la vue en plus de la vue tabulaire et de moi. Je voudrais déplacer le code lié à la vue Table vers sa propre classe. Pour ce faire, j'essaie ce qui suit:

  • Créer une nouvelle sous-classe de UITableViewController dans Xcode
  • Déplacer les implémentations connues de numberOfSectionsInTableView:, tableView:numberOfRowsInSection: et tableView:cellForRowAtIndexPath: à la nouvelle sous-classe
  • Faites glisser un UITableViewController vers le niveau supérieur du XIB existant dans InterfaceBuilder, supprimez les UIView/UITableView qui sont créés automatiquement pour cette UITableViewController, puis définissez la classe de UITableViewController pour qu'elle corresponde à la nouvelle sous-classe
  • Supprimez les connexions UITableView et dataSource existantes de delegate qui fonctionnaient précédemment et connectez-les aux nouvelles UITableViewController

Une fois terminé, je n'ai pas de UITableView de travail. Je me retrouve avec l'un des trois résultats qui peuvent apparemment se produire au hasard:

  • Lorsque le UITableView se charge, j'obtiens une erreur d'exécution indiquant que j'envoie tableView:cellForRowAtIndexPath: à un objet qui ne le reconnaît pas
  • Lorsque le UITableView se charge, le projet pénètre dans le débogueur sans erreur
  • Il n'y a pas d'erreur, mais le UITableView n'apparaît pas

Avec un débogage et ayant créé un projet de base juste pour reproduire ce problème, je vois généralement la 3ème option ci-dessus (pas d'erreur mais pas de vue de table visible). J'ai ajouté quelques appels NSLog et j'ai constaté que bien que numberOfSectionsInTableView: et numberOfRowsInSection: sont tous deux appelés, cellForRowAtIndexPath: n'est pas. Je suis convaincu que je manque quelque chose de vraiment simple et j'espérais que la réponse serait évidente pour quelqu'un avec plus d'expérience que moi. Si cela ne s'avère pas être une réponse facile, je serais heureux de mettre à jour avec du code ou un exemple de projet. Merci pour votre temps!

Suivez les étapes pour reproduire:

  • Créez un nouvel iPhone OS, une application basée sur la vue dans Xcode et appelez-le TableTest
  • Ouvert TableTestViewController.xib dans Interface Builder et faites glisser un UITableView sur la surface de vue fournie.
  • Connectez les prises UITableView et dataSource- de delegate au propriétaire du fichier, qui devrait déjà représenter la classe TableTestViewController-. Sauvegardez vos modifications
  • De retour dans Xcode, ajoutez le code suivant à TableTestViewController.m:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"Returning num sections");
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSLog(@"Returning num rows");
    return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Trying to return cell");
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }
    cell.text = @"Hello";
    NSLog(@"Returning cell");
    return cell;
}
  • Build and Go, et vous devriez voir le mot Hello apparaître dans le UITableView

  • Maintenant, pour tenter de déplacer la logique de cette UITableView vers une classe distincte, créez d'abord un nouveau fichier dans Xcode, en choisissant la sous-classe UITableViewController et en appelant la classe TableTestTableViewController

  • Supprimez l'extrait de code ci-dessus de TableTestViewController.m et placez-le dans TableTestTableViewController.m, en remplaçant l'implémentation par défaut de ces trois méthodes par la nôtre.
  • De retour dans Interface Builder dans le même TableTestViewController.xib-, faites glisser un UITableViewController dans la fenêtre principale de l'IB et supprimez le nouvel objet UITableView qui l'accompagne automatiquement
  • Définissez la classe de ce nouveau UITableViewController sur TableTestTableViewController
  • Supprimez les liaisons dataSource et delegate des UITableView existantes et fonctionnant précédemment et reconnectez les deux mêmes liaisons à la nouvelle TableTestTableViewController que nous avons créée .
  • Enregistrez les modifications, Build and Go, et si vous obtenez les résultats que j'obtiens, notez que UITableView ne fonctionne plus correctement

Solution: Avec un peu plus de dépannage et une assistance des iPhone Developer Forums , j'ai documenté une solution! La sous-classe principale UIViewController du projet a besoin d'une sortie pointant vers l'instance UITableViewController. Pour ce faire, ajoutez simplement ce qui suit à l'en-tête de la vue principale (TableTestViewController.h):

#import "TableTestTableViewController.h"

et

IBOutlet TableTestTableViewController *myTableViewController;

Ensuite, dans Interface Builder, connectez la nouvelle prise de File's Owner à TableTestTableViewController dans la fenêtre principale d'IB. Aucun changement n'est nécessaire dans la partie UI du XIB. Le simple fait d'avoir cette prise en place, même si aucun code utilisateur ne l'utilise directement, résout complètement le problème. Merci à ceux qui ont aidé et le mérite revient à BaldEagle sur les Forums des développeurs iPhone pour avoir trouvé la solution.

54
Adam Alexander

J'ai suivi vos étapes, recréé le projet et rencontré le même problème. En gros, vous y êtes presque. Il manque 2 choses (une fois corrigé ça marche):

  • Vous devez connecter le tableView du TableTestTableViewController au UITableView que vous avez à l'écran. Comme je l'ai dit précédemment car ce n'est pas IBOutlet, vous pouvez remplacer la propriété tableView et la faire et IBOutlet:

    @interface TableTestTableViewController : UITableViewController {
        UITableView *tableView;
    }
    
    @property (nonatomic, retain) IBOutlet UITableView *tableView;
    
  • La prochaine chose est d'ajouter une référence au TableTestTableViewController et de la conserver dans le TableTestViewController. Sinon, votre TableTestTableViewController peut être libéré (après avoir chargé la plume sans rien y accrocher.) Et c'est pourquoi vous voyez les résultats erratiques, les plantages ou rien ne s'affiche. Pour ce faire, ajoutez:

    @interface TableTestViewController : UIViewController {
        TableTestTableViewController *tableViewController;
    }
    
    @property (nonatomic, retain) IBOutlet TableTestTableViewController  *tableViewController;
    

    et connectez-le dans l'Interface Builder à l'instance TableTestTableViewController.

Avec ce qui précède, cela a bien fonctionné sur ma machine.

Je pense aussi qu'il serait bon de préciser la motivation derrière tout cela (au lieu d'utiliser simplement le UITableViewController avec son propre UITableView). Dans mon cas, il s'agissait d'utiliser d'autres vues que le UITableView sur le même écran de contenu. Je peux donc ajouter d'autres UILabels ou UIImages sous UIView et afficher le UITableView en dessous ou au-dessus d'eux.

23
keremk

Je viens de passer de nombreuses heures à arracher mes cheveux pour essayer de comprendre pourquoi un UITableView n'apparaîtrait pas quand je l'ai intégré dans une plume séparée au lieu de la plume principale. J'ai finalement trouvé votre discussion ci-dessus et j'ai réalisé que c'était parce que mon UITableViewController n'était pas conservé! Apparemment, les propriétés de délégué et de source de données de UITableView ne sont pas marquées "conserver" et donc ma plume était en cours de chargement mais le contrôleur était lancé ... Et en raison des merveilles de objective-c, j'ai eu non messages d'erreur du tout ... Je ne comprends toujours pas pourquoi il ne s'est pas bloqué. Je sais que j'ai déjà vu "un message envoyé au xxx" ... pourquoi ne m'en donnait-il pas un?!?

Je pense que la plupart des développeurs supposeraient que la structure qu'ils construisent dans un constructeur d'interface serait conservée dans un contexte plus large (la Nib) et non soumise à publication. Je suppose que je sais pourquoi ils font ça .. pour que l'iPhone puisse déposer et recharger des parties de la plume sur une mémoire faible. Mais l'homme, c'était difficile à comprendre.

Quelqu'un peut-il me dire où j'aurais dû lire ce comportement dans la documentation?

Aussi - sur le raccordement de la vue. Tout d'abord, si vous en faites glisser un depuis le générateur d'interface utilisateur, vous verrez qu'ils connectent la propriété view (qui est un IBOutlet) à la vue de table. Il n'est pas nécessaire d'exposer le tableView, qui semble être défini en interne. En fait, il ne semble même pas nécessaire de définir la vue sauf si vous voulez une notification viewDidLoad. Je viens de rompre la connexion de vue entre mon uitableview et uitableviewcontroller (uniquement délégué et ensemble de sources de données) et cela fonctionne apparemment bien.

5
Pat Niemeyer

Oui pour une raison quelconque (veuillez ajouter si quelqu'un sait pourquoi ...) tableView la propriété de UITableViewController n'est pas exposée en tant que IBOutlet même s'il s'agit d'une propriété publique. Ainsi, lorsque vous utilisez Interface Builder, vous ne pouvez pas voir cette propriété pour vous connecter à votre autre UITableView. Ainsi, dans votre sous-classe, vous pouvez créer une propriété tableView marquée comme IBOutlet et la connecter.

Tout cela me semble hacky et une solution de contournement, mais cela semble être le seul moyen de séparer un UITableViewController'sUITableView et le placer ailleurs dans la hiérarchie de l'interface utilisateur. Je suis tombé sur le même problème lorsque j'ai essayé de concevoir la vue où il y a d'autres choses que le UITableView et c'est ainsi que je l'ai résolu ... Est-ce la bonne approche ???

3
keremk

J'ai pu faire ce travail. J'ai construit un tableau de bord vraiment sympa avec 4 TableViews et une vue Web avec vidéo. La clé est d'avoir les contrôleurs tableView et les IBOutlets séparés vers les autres contrôleurs tableview définis dans le contrôleur de vue. Dans UIB, il vous suffit de connecter les autres contrôleurs tableview au propriétaire du fichier du contrôleur de vue. Connectez ensuite les tables aux contrôleurs de vue correspondants pour la source de données et le délégué.

2
user589642