Utilisation de id <protocole> pour le propriétaire du file dans Interface Builder?

J'ai un UITableViewCell personnalisé que UITableViewCell partir d'une plume en utilisant instantiateWithOwner:(id)owner options:(NSDictionary *)options . Lorsque la pointe est instanciée, je la sauvegarde dans un IBOutlet défini dans mon controller de vue, qui est défini en tant que propriétaire du file dans le file .xib. Tout a bien fonctionné.

J'ai maintenant rencontré le besoin d'utiliser cette cellule personnalisée dans plusieurs controllers de vue. J'espérais pouvoir définir un protocole (par exemple CustomCellOwner), que plusieurs controllers de vue pourraient implémenter. Le protocole définirait simplement l'IBOutlet utilisé pour referencer la cellule lors de l'instanciation.

Donc, idéalement, je voudrais définir "propriétaire du file" à:

 id <CustomCellOwner> 

dans Interface Builder.

Cependant, Interface Builder semble seulement vous permettre de définir le propriétaire du file à une class connue, pas à un identifiant implémentant un protocole?

Est-ce qu'il y a un moyen de faire ça? Ou, une façon plus simple d'aborder ce problème?

Merci!

Ce n'est pas la solution que vous requestz, mais vous pourriez créer une sous-class UIViewController que vous sous- UIViewController pour chaque controller de vue qui doit utiliser votre plume. Quelque chose comme:

 @interface CustomCellOwnerViewController : UIViewController @property (nonatomic, strong) IBOutlet UIButton *someButton; -(IBAction)doSomething; @end 

Et puis utilisez cela comme la class de base pour chacun:

 @interface FirstView : CustomCellOwnerViewController 

Ensuite, vous pouvez simplement définir File's Owner à CustomCellOwnerViewController sans problèmes.

Juste une idée.

Je suis tombé sur ceci aujourd'hui et n'ai pas trouvé une bonne solution. Je l'ai cependant bidouillé pour que ça fonctionne bien. Il se sent vraiment comme un hack si.

J'ai d'abord créé une class "fakeOwner" comme ceci:

 @interface fakeOwner : NSObject @property (nonatomic, assign) IBOutlet MyBaseCell* itemTableCell; @end @implementation fakeOwner @synthesize itemTableCell; @end 

J'ai ensuite défini le propriétaire de l'object dans le XIB comme fakeOwner et connecté la prise. Ensuite, pour chaque controller qui veut utiliser ces cellules, j'ajoute la même propriété et crée la class comme ceci:

  [[NSBundle mainBundle] loadNibNamed:@"MyBaseCell" owner:self options:nil]; MyBaseCell* itemCell = self.itemTableCell; self.itemTableCell = nil; 

Comme fakeOwner et mon controller ont la même IBOutlet, charger la cellule avec le controller en tant que propriétaire provoque la connection même si ce n'est pas ce qui est explicitement défini dans le XIB.

Pas 100% si la memory management est correcte pour le moment (je pense que ça va), mais à part ça ça a l'air de bien fonctionner. J'aimerais voir une meilleure façon de faire cela.

Faire un faux propriétaire fonctionnera; cependant, une telle solution peut être fragile et inextensible. Dans un sens, la cellule se possède, mais même cela est techniquement incorrect. La vérité est que les UITableViewCell n'ont pas de propriétaires.

La meilleure façon d'implémenter des cellules de vue de table personnalisées est de créer d'abord une sous-class personnalisée de UITableViewCell. Dans cette class, vous allez définir tous les IBOutlets et autres pour la cellule. Voici un exemple de file d'en-tête:

 @interface RBPersonCell : UITableViewCell @property (nonatomic, strong) IBOutlet UILabel * nameLabel; @property (nonatomic, strong) IBOutlet UILabel * ageLabel; - (void)setupWithPerson:(Person *)person; @end 

De là, j'ai une méthode pratique qui crée la cellule à partir de la plume, si nécessaire:

 + (id)cellForTableView:(UITableView *)tableView reuseIdentifier:(NSSsortingng *)reuseID fromNib:(UINib *)nib { if (!reuseID) reuseID = [self cellIdentifier]; id cell = [tableView dequeueReusableCellWithIdentifier:reuseID]; if (!cell) { NSArray * nibObjects = [nib instantiateWithOwner:nil options:nil]; // Sanity check. NSAssert2(([nibObjects count] > 0) && [[nibObjects objectAtIndex:0] isKindOfClass:[self class]], @"Nib '%@' does not appear to contain a valid %@", [self nibName], NSSsortingngFromClass([self class])); cell = [nibObjects objectAtIndex:0]; } return cell; } 

Cette méthode encapsule tout le code de création, donc je n'ai jamais besoin de le voir ou de le réécrire. Il suppose que la cellule personnalisée est la première vue racine dans la plume. C'est une hypothèse assez sûre puisque vous ne devriez avoir la cellule personnalisée que comme vue racine.

Avec tout ce code en place, vous êtes prêt à travailler dans Interface Builder. Vous devez d'abord définir la class personnalisée dans l'inspection d'identité. Ensuite, n'oubliez pas de définir votre identifiant de cellule. Pour plus de commodité, il est préférable d'utiliser le nom de la class personnalisée. Lorsque vous faites glisser vos connections, au lieu de les faire glisser vers le propriétaire du file, faites glisser vos connections vers la cellule personnalisée elle-même.

La plupart de ce que j'ai appris sur les cellules de vue de table personnalisé provient des recettes iOS Recettes 15-16. Voici un extrait gratuit directement de The Pragmatic Bookshelf . Vous pouvez consulter ce livre pour plus de détails.

MODIFIER:

J'ai finalement réussi à ouvrir ma class RBSmartTableViewCell . Vous pouvez le find sur mon GitHub . Vous devriez find cette class plus utile que le code directement depuis iOS Recipes, car ma class traite toutes les cellules de la même manière, qu'elles soient construites en utilisant XIBs, UIStoryboard ou le code. Ce repo comprend également des échantillons de travail.

Dans iOS 5.0, il y a maintenant la méthode registerNib:forCellReuseIdentifier: sur UITableView qui, je crois, tente de résoudre un problème similaire.

De la documentation:

Lorsque vous enregistrez un object nib avec la vue tabulaire et appelez ultérieurement la méthode dequeueReusableCellWithIdentifier: transmettant l'identificateur enregistré, la vue tabulaire instancie la cellule de l'object nib si elle n'est pas déjà dans la queue de réutilisation.

Cela pourrait être une approche alternative en fonction de vos besoins.

Une autre option pourrait être de créer un object 'factory' léger qui gère la création des cellules pour vous. Cet object serait FilesOwner dans le constructor d'interface, avec la sortie rootObject correctement définie.

 @interface NibLoader : NSObject @property (nonatomic, strong) UINib * nib; @property (nonatomic, strong) IBOutlet id rootObject; - (id)initWithNibName:(NSSsortingng *)name bundle:(NSBundle *)bundleOrNil; - (id)instantiateRootObject; @end @implementation NibLoader @synthesize nib, rootObject; - (id)initWithNibName:(NSSsortingng *)name bundle:(NSBundle *)bundleOrNil { self = [super init]; if (self) { self.nib = [UINib nibWithNibName:name bundle:bundleOrNil]; } return self; } - (id)instantiateRootObject { self.rootObject = nil; [self.nib instantiateWithOwner:self options:nil]; NSAssert(self.rootObject != nil, @"NibLoader: Nib did not set rootObject."); return self.rootObject; } @end 

Ensuite, dans les controllers de vue:

 NibLoader *customCellLoader = [[NibLoader alloc] initWithNibName:@"CustomCell" bundle:nil]; self.customCell = customCellLoader.instantiateRootObject; 

Je préfère définir explicitement l'object racine au lieu de chercher dans le tableau renvoyé par instantiateWithOwner:options: parce que je sais que la position des objects dans ce tableau a changé dans le passé.