objective-c - tutorial - uitableview xcode




Dans un storyboard, comment créer une cellule personnalisée pour une utilisation avec plusieurs contrôleurs? (4)

BJ Homer a donné une excellente explication de ce qui se passe.

D'un point de vue pratique, j'ajouterai que, étant donné que vous ne pouvez pas avoir de cellules comme xibs ET connect segues, le meilleur choix est d'avoir la cellule comme xib - les transitions sont beaucoup plus faciles à maintenir que les configurations de cellules et les propriétés , et vos segues sont susceptibles d'être différentes de vos différents contrôleurs de toute façon. Vous pouvez définir le segue directement depuis votre contrôleur de vue de table vers le contrôleur suivant, et l'exécuter en code. .

Une autre remarque est que le fait d'avoir votre cellule dans un fichier xib séparé vous empêche de connecter toute action, etc. directement au contrôleur de vue de la table (je n'ai pas compris cela - vous ne pouvez pas définir le propriétaire du fichier comme significatif ). Je travaille autour de ceci en définissant un protocole que le contrôleur de vue de table de la cellule doit se conformer et en ajoutant le contrôleur comme une propriété faible, semblable à un délégué, dans cellForRowAtIndexPath.

J'essaie d'utiliser des storyboards dans une application sur laquelle je travaille. Dans l'application il y a des listes et des utilisateurs et chacun contient une collection de l'autre (les membres d'une liste, les listes possédées par un utilisateur). Donc, en conséquence, j'ai des classes ListCell et UserCell . L'objectif est de les rendre réutilisables dans toute l'application (c.-à-d. Dans n'importe lequel de mes contrôleurs de table).

C'est là que je rencontre un problème.

Comment puis-je créer une cellule de vue de table personnalisée dans le storyboard qui peut être réutilisée dans n'importe quel contrôleur de vue?

Voici les choses spécifiques que j'ai essayées jusqu'ici.

  • Dans Controller # 1, UITableViewCell une cellule prototype, définissez la classe sur ma sous-classe UITableViewCell , définissez l'ID de réutilisation, UITableViewCell les étiquettes et les UITableViewCell aux prises de la classe. Dans le contrôleur n ° 2, ajoutez une cellule prototype vide, définissez-la sur la même classe et réutilisez l'ID comme précédemment. Lorsqu'il s'exécute, les étiquettes n'apparaissent jamais lorsque les cellules sont affichées dans le contrôleur n ° 2. Fonctionne bien dans le contrôleur n ° 1.

  • Conçu chaque type de cellule dans un NIB différent et câblé jusqu'à la classe de cellule appropriée. Dans le storyboard, ajoutez une cellule prototype vide et définissez son identifiant de classe et de réutilisation pour faire référence à ma classe de cellule. Dans les méthodes viewDidLoad des contrôleurs, ces fichiers NIB ont été enregistrés pour l'ID de réutilisation. Lorsque montré, les cellules dans les deux contrôleurs étaient vides comme le prototype.

  • Les prototypes conservés dans les deux contrôleurs sont vides et définissent la classe et réutilisent l'identifiant dans ma classe de cellule. Construit l'interface utilisateur des cellules entièrement en code. Les cellules fonctionnent parfaitement dans tous les contrôleurs.

Dans le second cas, je soupçonne que le prototype est toujours en train de surcharger la NIB et si je tue les cellules prototypes, l'enregistrement de mon NIB pour l'id de réutilisation fonctionnerait. Mais alors, je ne serais pas capable d'installer des séquences depuis les cellules vers d'autres images, ce qui est vraiment l'avantage d'utiliser des storyboards.

À la fin de la journée, je veux deux choses: câbler les flux basés sur tableview dans le storyboard et définir les dispositions de cellules visuellement plutôt que dans le code. Je ne vois pas comment les obtenir tous les deux.


J'ai trouvé un moyen de charger la cellule pour le même VC, pas testé pour les segues. Cela pourrait être une solution de contournement pour créer la cellule dans une plume séparée

Disons que vous avez un VC et 2 tables et vous voulez concevoir une cellule dans le storyboard et l'utiliser dans les deux tableaux.

(ex: une table et un champ de recherche avec un UISearchController avec une table pour les résultats et vous voulez utiliser la même cellule dans les deux)

Lorsque le contrôleur demande la cellule, faites ceci:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * identifier = @"CELL_ID";

    ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
  // Ignore the "tableView" argument
}

Et ici vous avez votre cellule du storyboard


Malgré la bonne réponse de BJ Homer, j'ai l'impression d'avoir une solution. En ce qui concerne mes tests, cela fonctionne.

Concept: Créez une classe personnalisée pour la cellule xib. Là, vous pouvez attendre un événement tactile et effectuer le segue par programmation. Maintenant tout ce dont nous avons besoin est une référence au contrôleur exécutant la Segue. Ma solution est de le définir dans tableView:cellForRowAtIndexPath:

Exemple

J'ai un DetailedTaskCell.xib contenant une cellule de tableau que je voudrais utiliser dans plusieurs vues de table:

Il existe une classe personnalisée TaskGuessTableCell pour cette cellule:

C'est là que la magie se produit.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end

// TashGuessTableCell.m
#import "TaskGuessTableCell.h"

@implementation TaskGuessTableCell

@synthesize controller;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSIndexPath *path = [controller.tableView indexPathForCell:self];
    [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
    [super touchesEnded:touches withEvent:event];
}

@end

J'ai plusieurs Segues mais elles ont toutes le même nom: "FinishedTask" . Si vous avez besoin d'être flexible ici, je suggère d'ajouter une autre propriété.

Le ViewController ressemble à ceci:

// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"

@implementation LogbookViewController

- (void)viewDidLoad
{
    [super viewDidLoad]

    // register custom nib
    [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TaskGuessTableCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
    cell.controller = self; // <-- the line that matters
    // if you added the seque property to the cell class, set that one here
    // cell.segue = @"TheSegueYouNeedToTrigger";
    cell.taskTitle.text  = [entry title];
    // set other outlet values etc. ...

    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"FinishedTask"])
    {
        // do what you have to do, as usual
    }

}

@end

Il pourrait y avoir des moyens plus élégants pour atteindre la même chose mais - ça marche! :)


Si je comprends bien, vous voulez:

  1. Concevoir une cellule dans IB qui peut être utilisée dans plusieurs scènes de storyboard.
  2. Configurez des séquences de storyboard uniques à partir de cette cellule, en fonction de la scène dans laquelle se trouve la cellule.

Malheureusement, il n'y a actuellement aucun moyen de le faire. Pour comprendre pourquoi vos tentatives précédentes n'ont pas fonctionné, vous devez en savoir plus sur le fonctionnement des storyboards et des cellules prototype. (Si vous ne vous souciez pas de savoir pourquoi ces autres tentatives n'ont pas fonctionné, n'hésitez pas à partir maintenant.J'ai pas de solutions magiques pour vous, autre que de suggérer que vous déposez un bug.)

Un storyboard n'est, en substance, pas beaucoup plus qu'une collection de fichiers .xib. Lorsque vous chargez un contrôleur de vue de table contenant des prototypes de storyboard, voici ce qui se passe:

  • Chaque cellule prototype est en fait sa propre mini-plume intégrée. Ainsi, lorsque le contrôleur de vue de table se charge, il parcourt chacune des plumes et des appels de la cellule prototype -[UITableView registerNib:forCellReuseIdentifier:] .
  • La vue de la table demande au contrôleur les cellules.
  • Vous appelez probablement -[UITableView dequeueReusableCellWithIdentifier:]
  • Lorsque vous demandez une cellule avec un identifiant de réutilisation donné, elle vérifie si une puce a été enregistrée. Si c'est le cas, il instancie une instance de cette cellule. Ceci est composé des étapes suivantes:

    1. Regardez la classe de la cellule, telle que définie dans la plume de la cellule. Appelez [[CellClass alloc] initWithCoder:] .
    2. La méthode -initWithCoder: passe à travers et ajoute des sous-vues et définit les propriétés qui ont été définies dans la plume. ( IBOutlet est probablement connecté ici, bien que je ne l'ai pas testé, ça peut arriver dans -awakeFromNib )
  • Vous configurez votre cellule comme vous le souhaitez.

La chose importante à noter ici est qu'il existe une distinction entre la classe de la cellule et l' apparence visuelle de la cellule. Vous pouvez créer deux cellules prototypes distinctes de la même classe, mais avec leurs sous-vues présentées différemment. En fait, si vous utilisez les styles UITableViewCell par défaut, c'est exactement ce qui se passe. Le style "Default" et le style "Subtitle", par exemple, sont tous deux représentés par la même classe UITableViewCell .

Ceci est important : la classe de la cellule n'a pas de corrélation bi-univoque avec une hiérarchie de vue particulière. La hiérarchie de vue est entièrement déterminée par ce qui se trouve dans la cellule prototype enregistrée avec ce contrôleur particulier.

Notez également que l'identificateur de réutilisation de la cellule n'a pas été enregistré dans un dispensaire cellulaire mondial. L'identifiant de réutilisation est uniquement utilisé dans le contexte d'une seule instance UITableView .

Compte tenu de cette information, regardons ce qui s'est passé dans vos tentatives ci-dessus.

Dans Controller # 1, ajoutez une cellule prototype, définissez la classe sur ma sous-classe UITableViewCell, définissez l'ID de réutilisation, ajoutez les étiquettes et les connectez aux prises de la classe. Dans le contrôleur n ° 2, ajoutez une cellule prototype vide, définissez-la sur la même classe et réutilisez l'ID comme précédemment. Lorsqu'il s'exécute, les étiquettes n'apparaissent jamais lorsque les cellules sont affichées dans le contrôleur n ° 2. Fonctionne bien dans le contrôleur n ° 1.

C'est prévu. Bien que les deux cellules aient la même classe, la hiérarchie de vue transmise à la cellule dans le contrôleur n ° 2 était entièrement dépourvue de sous-vues. Vous avez donc une cellule vide, ce qui est exactement ce que vous avez mis dans le prototype.

Conçu chaque type de cellule dans un NIB différent et câblé jusqu'à la classe de cellule appropriée. Dans le storyboard, ajoutez une cellule prototype vide et définissez son identifiant de classe et de réutilisation pour faire référence à ma classe de cellule. Dans les méthodes viewDidLoad des contrôleurs, ces fichiers NIB ont été enregistrés pour l'ID de réutilisation. Lorsque montré, les cellules dans les deux contrôleurs étaient vides comme le prototype.

Encore une fois, c'est prévu. L'identifiant de réutilisation n'est pas partagé entre les scènes de storyboard ou les nibs, de sorte que le fait que toutes ces cellules distinctes aient le même identifiant de réutilisation n'a aucun sens. La cellule que vous récupérez à partir de la vue de table aura une apparence qui correspond à la cellule prototype dans cette scène du storyboard.

Cette solution était proche, cependant. Comme vous l'avez noté, vous pouvez simplement appeler par programme -[UITableView registerNib:forCellReuseIdentifier:] , en passant l' UINib contenant la cellule, et vous obtiendrez cette même cellule. (Ce n'est pas parce que le prototype "écrasait" la plume, vous n'aviez simplement pas enregistré la plume avec la tableview, donc il regardait toujours la plume incorporée dans le storyboard.) Malheureusement, il y a une faille dans cette approche - il n'y a aucun moyen de brancher des enchaînements storyboard à une cellule dans une plume autonome.

Les prototypes conservés dans les deux contrôleurs sont vides et définissent la classe et réutilisent l'identifiant dans ma classe de cellule. Construit l'interface utilisateur des cellules entièrement en code. Les cellules fonctionnent parfaitement dans tous les contrôleurs.

Naturellement. J'espère que ce n'est pas surprenant.

Donc, c'est pourquoi ça n'a pas marché. Vous pouvez concevoir vos cellules dans des plumes autonomes et les utiliser dans plusieurs scènes de storyboard; vous ne pouvez pas actuellement connecter des séquences de storyboard à ces cellules. Heureusement, vous avez appris quelque chose en lisant ceci.





storyboard