Comment chargez-vous des UITableViewCells personnalisées à partir de fichiers Xib?

293

La question est simple: comment charger des UITableViewCellfichiers personnalisés à partir de fichiers Xib? Cela vous permet d'utiliser Interface Builder pour concevoir vos cellules. La réponse n'est apparemment pas simple en raison de problèmes de gestion de la mémoire. Ce fil mentionne le problème et suggère une solution, mais est pré-version NDA et manque de code. Voici un long fil conducteur qui discute du problème sans fournir de réponse définitive.

Voici un code que j'ai utilisé:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

Pour utiliser ce code, créez MyCell.m / .h, une nouvelle sous-classe de UITableViewCellet ajoutez IBOutletspour les composants souhaités. Créez ensuite un nouveau fichier "Empty XIB". Ouvrez le fichier Xib dans IB, ajoutez un UITableViewCellobjet, définissez son identifiant sur "MyCellIdentifier" et définissez sa classe sur MyCell et ajoutez vos composants. Enfin, connectez le IBOutletsaux composants. Notez que nous n'avons pas défini le propriétaire du fichier dans IB.

D'autres méthodes préconisent de définir le propriétaire du fichier et avertissent des fuites de mémoire si le Xib n'est pas chargé via une classe d'usine supplémentaire. J'ai testé ce qui précède sous Instruments / Leaks et je n'ai vu aucune fuite de mémoire.

Alors, quelle est la manière canonique de charger des cellules à partir de Xibs? Définissons-nous le propriétaire du fichier? Avons-nous besoin d'une usine? Si oui, à quoi ressemble le code de l'usine? S'il existe plusieurs solutions, clarifions les avantages et les inconvénients de chacune d'entre elles ...

DrGary
la source
2
Quelqu'un peut-il modifier le sujet pour poser la question, c'est-à-dire "Comment charger des UITableViewCells personnalisés à partir de fichiers Xib?" (Ignorer si cela n'est tout simplement pas possible sur stackoverflow.)
Steven Fisher
1
Pour iOS 5 et au-delà, voici la solution: stackoverflow.com/questions/15591364/… , qui est la même que la solution de giuseppe.
Matt Becker
Note rapide, plus simple (milieu 2013) répondez ici stackoverflow.com/questions/15378788/… jamihash
Fattie

Réponses:

288

Voici deux méthodes qui, selon l' auteur d'origine, ont été recommandées par un ingénieur de l'IB .

Voir le message réel pour plus de détails. Je préfère la méthode # 2 car elle semble plus simple.

Méthode n ° 1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

Méthode n ° 2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Mise à jour (2014): la méthode # 2 est toujours valide mais il n'y a plus de documentation pour elle. Auparavant, il figurait dans les documents officiels, mais il est maintenant supprimé au profit des story-boards.

J'ai publié un exemple de travail sur Github:
https://github.com/bentford/NibTableCellExample

modifier pour Swift 4.2

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}
Bentford
la source
1
Pour la méthode 1, ne devriez-vous pas faire quelque chose comme "cell = (BDCustomCell *) [[temporaireController.view conserver] autorelease];" donc la cellule n'est pas libérée lorsque le contrôleur temporaire est libéré?
Tod Cunningham
Hm. La documentation qui parle de # 2 vous dit toujours de définir le propriétaire de la cellule dans le fichier XIB, sur une classe de contrôleur connue. Cela n'a peut-être pas d'importance lorsque vous définissez le propriétaire lors du chargement.
Oscar
@OscarGoldman Le propriétaire de la cellule dans le fichier XIB est une classe (c'est-à-dire le type de propriétaire). Le propriétaire de la cellule dans loadNibNamed: owner: options: est un objet du type spécifié dans le XIB.
bentford
2
@CoolDocMan Option # 2 fonctionne toujours. Le problème est le plus probable avec la plume. Voici un exemple: github.com/bentford/NibTableCellExample
bentford
2
Pourquoi ce super vieux code est-il si élevé? Stackoverflow fait quelque chose: /
Nico S.
304

La bonne solution est la suivante:

- (void)viewDidLoad
{
    [super viewDidLoad];
    UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of ItemCell
    PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    return cell;
}
giuseppe
la source
cela va-t-il casser les applications iOS5? Je n'ai vraiment jamais vu UINib
Adam Waite
@AdamWaite L'enregistrement des fichiers NIB fonctionne pour iOS 5 et versions ultérieures, donc il ne casse pas les applications iOS 5. Et UINib existe même depuis iOS 4.
Mecki
Pour un bon exemple, consultez le référentiel git référencé dans la réponse supérieure ici: stackoverflow.com/questions/18746929/…
netigger
39

S'inscrire

Après iOS 7, ce processus a été simplifié jusqu'à ( swift 3.0 ):

// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")

// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")

( Remarque ) Ceci est également possible en créant les cellules dans le .xibou les .stroyboardfichiers, en tant que cellules prototypes. Si vous devez leur attacher une classe, vous pouvez sélectionner le prototype de cellule et ajouter la classe correspondante (doit être un descendant de UITableViewCell, bien sûr).

Dequeue

Et plus tard, retiré de la file d'attente en utilisant ( swift 3.0 ):

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text = "Hello"

    return cell
}

La différence étant que cette nouvelle méthode non seulement extrait la cellule de la file d'attente, elle crée également si elle est inexistante (cela signifie que vous n'avez pas à faire de if (cell == nil)manigances) et que la cellule est prête à être utilisée comme dans l'exemple ci-dessus.

( Attention ) tableView.dequeueReusableCell(withIdentifier:for:)a le nouveau comportement, si vous appelez l'autre (sans indexPath:) vous obtenez l'ancien comportement, dans lequel vous devez vérifier nilet l'instaurer vous-même, notez la UITableViewCell?valeur de retour.

if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
    // Cell be casted properly
    cell.myCustomProperty = true
}
else
{
    // Wrong type? Wrong identifier?
}

Et bien sûr, le type de la classe associée de la cellule est celui que vous avez défini dans le fichier .xib pour la UITableViewCellsous - classe, ou bien en utilisant l'autre méthode de registre.

Configuration

Idéalement, vos cellules ont déjà été configurées en termes d'apparence et de positionnement de contenu (comme les étiquettes et les vues d'images) au moment où vous les avez enregistrées, et selon la cellForRowAtIndexPathméthode que vous remplissez simplement.

Tous ensemble

class MyCell : UITableViewCell
{
    // Can be either created manually, or loaded from a nib with prototypes
    @IBOutlet weak var labelSomething : UILabel? = nil
}

class MasterViewController: UITableViewController 
{
    var data = ["Hello", "World", "Kinda", "Cliche", "Though"]

    // Register
    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
        // or the nib alternative
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    // Dequeue
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell

        cell.labelSomething?.text = data[indexPath.row]

        return cell
    }
}

Et bien sûr, tout cela est disponible dans ObjC avec les mêmes noms.

Pouvez
la source
Voici la version objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb
33

A pris la réponse de Shawn Craver et l'a nettoyée un peu.

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

+ (BBCell *)cellFromNibNamed:(NSString *)nibName;

@end

BBCell.m:

#import "BBCell.h"

@implementation BBCell

+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    BBCell *customCell = nil;
    NSObject* nibItem = nil;
    while ((nibItem = [nibEnumerator nextObject]) != nil) {
        if ([nibItem isKindOfClass:[BBCell class]]) {
            customCell = (BBCell *)nibItem;
            break; // we have a winner
        }
    }
    return customCell;
}

@end

Je crée toutes les sous-classes de BBCell de UITableViewCell, puis je remplace la norme

cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];

avec:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
vilcsak
la source
16

J'ai utilisé la méthode n ° 2 de bentford :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Cela fonctionne, mais faites attention aux connexions au propriétaire du fichier dans votre fichier UITableViewCell .xib personnalisé.

En transmettant owner:selfvotre loadNibNameddéclaration, vous définissez le en UITableViewControllertant que propriétaire du fichier de votre UITableViewCell.

Si vous faites un glisser-déposer vers le fichier d'en-tête dans IB pour configurer des actions et des sorties, il les définira par défaut comme propriétaire du fichier.

Dans loadNibNamed:owner:options, le code d'Apple tentera de définir des propriétés sur votre UITableViewController, puisque c'est le propriétaire. Mais vous n'avez pas défini ces propriétés, vous obtenez donc une erreur sur la conformité au codage des valeurs de clé :

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'

Si un événement est déclenché à la place, vous obtiendrez une NSInvalidArgumentException:

-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language:  auto; currently objective-c

Une solution de contournement simple consiste à pointer vos connexions Interface Builder au UITableViewCelllieu du propriétaire du fichier:

  1. Faites un clic droit sur le propriétaire du fichier pour afficher la liste des connexions
  2. Faites une capture d'écran avec Command-Shift-4 (faites glisser pour sélectionner la zone à capturer)
  3. x les connexions du propriétaire du fichier
  4. Cliquez avec le bouton droit sur UITableCell dans la hiérarchie d'objets et ajoutez à nouveau les connexions.
funroll
la source
J'ai eu le problème que vous avez mentionné, mais comment pointer les connexions vers UITableViewCell au lieu du propriétaire du fichier? Je ne comprends pas vos étapes, par exemple pourquoi une capture d'écran est-elle nécessaire? et quand j'ai cliqué sur le bouton ajouter à côté de la prise, rien ne se passe
xu huanze
@xuhuanze J'ai suggéré de prendre une capture d'écran afin que vous ayez un enregistrement des choses auxquelles le propriétaire du fichier était déjà connecté. Ensuite, vous pouvez recréer ces mêmes connexions. Vous devez faire glisser et déposer pour ajouter les connexions - pas seulement un simple clic.
funroll
Merci beaucoup, j'ai rencontré le problème "cette classe n'est pas conforme au codage des valeurs clés pour la clé" et je l'ai résolu grâce à votre aide. Je veux dire aux autres que vous devez également changer une classe de votre UITableViewCell en votre classe, que vous utilisez comme classe de cellule personnalisée.
Denis Kutlubaev
14

J'ai décidé de poster car je n'aime aucune de ces réponses - les choses peuvent toujours être plus simples et c'est de loin la manière la plus concise que j'ai trouvée.

1. Construisez votre Xib dans Interface Builder comme vous l'aimez

  • Définissez le propriétaire du fichier sur la classe NSObject
  • Ajoutez un UITableViewCell et définissez sa classe sur MyTableViewCellSubclass - si votre IB se bloque (se produit dans Xcode> 4 au moment de la rédaction de cet article), utilisez simplement une UIView de faire l'interface dans Xcode 4 si vous l'avez toujours autour
  • Disposez vos sous-vues dans cette cellule et attachez vos connexions IBOutlet à votre @interface dans le .h ou le .m (.m est ma préférence)

2. Dans votre sous-classe UIViewController ou UITableViewController

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. Dans votre MyTableViewCellSubclass

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}
webstersx
la source
9

Si vous utilisez Interface Builder pour créer des cellules, vérifiez que vous avez défini l'identifiant dans l'inspecteur. Vérifiez ensuite que c'est la même chose lorsque vous appelez dequeueReusableCellWithIdentifier.

J'ai accidentellement oublié de définir des identifiants dans un projet à table lourde, et le changement de performance était comme le jour et la nuit.

Alex R. Young
la source
8

Le chargement des UITableViewCells à partir des XIB économise beaucoup de code, mais entraîne généralement une vitesse de défilement horrible (en fait, ce n'est pas le XIB mais l'utilisation excessive des UIViews qui en est la cause).

Je vous suggère de jeter un œil à ceci: Référence du lien

Can Berk Güder
la source
6

Voici la méthode de classe que j'ai utilisée pour créer des cellules personnalisées à partir de XIB:

+ (CustomCell*) createNewCustomCellFromNib {

    NSArray* nibContents = [[NSBundle mainBundle]
                            loadNibNamed:@"CustomCell" owner:self options:NULL];

    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    CustomCell *customCell= nil;
    NSObject* nibItem = nil;

    while ( (nibItem = [nibEnumerator nextObject]) != nil) {

        if ( [nibItem isKindOfClass: [CustomCell class]]) {
            customCell = (CustomCell*) nibItem;

            if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                break; // we have a winner
            }
            else
                fuelEntryCell = nil;
        }
    }
    return customCell;
}

Ensuite, dans le XIB, j'ai défini le nom de classe et l'identifiant de réutilisation. Après cela, je peux simplement appeler cette méthode dans mon contrôleur de vue au lieu de

[[UITableViewCell] alloc] initWithFrame:]

Il est assez rapide et utilisé dans deux de mes applications d'expédition. C'est plus fiable que d'appeler [nib objectAtIndex:0], et dans mon esprit au moins, plus fiable que l'exemple de Stephan Burlot parce que vous êtes assuré de ne saisir qu'une vue d'un XIB qui est le bon type.

Shawn Craver
la source
5

La bonne solution est la suivante

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell  *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
    return cell; 
    }
Hamiz Ahmed
la source
4

Le rechargement de la NIB coûte cher. Mieux vaut le charger une fois, puis instancier les objets lorsque vous avez besoin d'une cellule. Notez que vous pouvez ajouter UIImageViews, etc. à la pointe, même plusieurs cellules, en utilisant cette méthode (Apple "registerNIB" iOS5 autorise un seul objet de niveau supérieur - Bogue 10580062 "iOS5 tableView registerNib: trop restrictif"

Donc, mon code est ci-dessous - vous lisez une fois dans la NIB (en initialisant comme je l'ai fait ou dans viewDidload - peu importe. À partir de là, vous instanciez la nib en objets puis choisissez celui dont vous avez besoin. C'est beaucoup plus efficace que de charger la nib encore et encore.

static UINib *cellNib;

+ (void)initialize
{
    if(self == [ImageManager class]) {
        cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
        assert(cellNib);
    }
}

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(cell == nil) {
        NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
        NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                            {
                                UITableViewCell *cell = (UITableViewCell *)obj;
                                return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                            } ];
        assert(idx != NSNotFound);
        cell = [topLevelItems objectAtIndex:idx];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];

    return cell;
}
David H
la source
4

Vérifiez ceci - http://eppz.eu/blog/custom-uitableview-cell/ - moyen très pratique en utilisant une petite classe qui termine une ligne dans la mise en œuvre du contrôleur:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}

entrez la description de l'image ici

Geri Borbás
la source
3

La façon correcte de le faire est de créer une implémentation, un en-tête et un XIB de sous-classe UITableViewCell. Dans le XIB, supprimez toutes les vues et ajoutez simplement une cellule de tableau. Définissez la classe comme nom de la sous-classe UITableViewCell. Pour le propriétaire du fichier, définissez-le comme nom de classe de sous-classe UITableViewController. Connectez le propriétaire du fichier à la cellule à l'aide de la sortie tableViewCell.

Dans le fichier d'en-tête:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

Dans le fichier d'implémentation:

@synthesize tableViewCell = _tableViewCell;

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}
Cameron Lowell Palmer
la source
3

Ce que je fais pour cela, c'est déclarer un IBOutlet UITableViewCell *celldans votre classe de contrôleur. Ensuite, appelez la NSBundle loadNibNamedméthode de classe, qui alimentera UITableViewCellla cellule déclarée ci-dessus.

Pour le xib, je vais créer un xib vide et ajouter l' UITableViewCellobjet dans IB où il peut être configuré selon les besoins. Cette vue est ensuite connectée à la cellule IBOutletde la classe de contrôleur.

- (UITableViewCell *)tableView:(UITableView *)table
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@ loading RTEditableCell.xib", [self description] );

    static NSString *MyIdentifier = @"editableCellIdentifier";
    cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];

    if(cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                      owner:self
                                    options:nil];
    }

    return cell;
}

Ajouts NSBundle loadNibNamed (connexion ADC)

article de cocoawithlove.com dont je me suis procuré le concept (obtenez l'exemple d'application des numéros de téléphone)

Ryan Townshend
la source
3
  1. Créez votre propre AbcViewCellsous- classe de classe personnalisée à partir de UITableViewCell(assurez-vous que le nom de votre fichier de classe et le nom du fichier nib sont identiques)

  2. Créez cette méthode de classe d'extension.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
  3. Utilise le.

    let cell: AbcViewCell = UITableViewCell.fromNib()

William Hu
la source
2

Importez d'abord votre fichier de cellule personnalisé #import "CustomCell.h", puis modifiez la méthode déléguée comme indiqué ci-dessous:

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

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

     return cell;
}
Mohit
la source
2

Dans Swift 4.2 et Xcode 10

J'ai trois fichiers de cellules XIB

dans ViewDidLoad enregistrez vos fichiers XIB comme ceci ...

Ceci est la première approche

tableView.register(UINib.init(nibName: "XIBCell", bundle: nil), forCellReuseIdentifier: "cell1")
tableView.register(UINib.init(nibName: "XIBCell2", bundle: nil), forCellReuseIdentifier: "cell2")
//tableView.register(UINib.init(nibName: "XIBCell3", bundle: nil), forCellReuseIdentifier: "cell3")

La deuxième approche enregistre directement les fichiers XIB dans cellForRowAt indexPath:

Voici mes fonctions de délégué tableview

//MARK: - Tableview delegates
override func numberOfSections(in tableView: UITableView) -> Int {

    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 6
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //This is first approach
    if indexPath.row == 0 {//Load first XIB cell
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! XIBCell
        return placeCell
    //Second approach
    } else if indexPath.row == 5 {//Load XIB cell3
        var cell = tableView.dequeueReusableCell(withIdentifier:"cell3") as? XIBCell3
        if cell == nil{
            let arrNib:Array = Bundle.main.loadNibNamed("XIBCell3",owner: self, options: nil)!
            cell = arrNib.first as? XIBCell3
        }

        //ADD action to XIB cell button
        cell?.btn.tag = indexPath.row//Add tag to button
        cell?.btn.addTarget(self, action: #selector(self.bookbtn1(_:)), for: .touchUpInside);//selector

        return cell!
    //This is first approach
    } else {//Load XIB cell2
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell2") as! XIBCell2

        return placeCell
    }

}
iOS
la source
1

Voici ma méthode pour cela: Chargement personnalisé UITableViewCells de fichiers ... XIB Une autre méthode

L'idée est de créer une sous-classe SampleCell du UITableViewCellavec une IBOutlet UIView *contentpropriété et une propriété pour chaque sous-vue personnalisée que vous devez configurer à partir du code. Puis pour créer un fichier SampleCell.xib. Dans ce fichier nib, changez le propriétaire du fichier en SampleCell. Ajoutez un contenu UIViewadapté à vos besoins. Ajoutez et configurez toutes les sous-vues (étiquette, vues d'image, boutons, etc.) que vous souhaitez. Enfin, liez la vue du contenu et les sous-vues au propriétaire du fichier.

MonsieurDart
la source
1

Voici une approche universelle pour l'enregistrement des cellules dans UITableView:

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

extension UITableViewCell: Reusable { }

extension UITableView {

func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
    let bundle = Bundle(for: cellClass.self)
    if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
        let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
        register(nib, forCellReuseIdentifier: cellClass.reuseID)
    } else {
        register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
    }
}

Explication:

  1. ReusableLe protocole génère l'ID de cellule à partir de son nom de classe. Assurez-vous de suivre la convention: cell ID == class name == nib name.
  2. UITableViewCellconforme au Reusableprotocole.
  3. UITableView l'extension résume la différence d'enregistrement des cellules via nib ou classe.

Exemple d'utilisation:

override func viewDidLoad() {
    super.viewDidLoad()
    let tableView = UITableView()
    let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
    cellClasses.forEach(tableView.register)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
    ...
    return cell
}
Vadim Bulavin
la source
0

Je ne sais pas s'il existe une méthode canonique, mais voici ma méthode:

  • Créer un xib pour un ViewController
  • Définissez la classe File Owner sur UIViewController
  • Supprimer la vue et ajouter un UITableViewCell
  • Définissez la classe de votre UITableViewCell sur votre classe personnalisée
  • Définissez l'identifiant de votre UITableViewCell
  • Réglez la sortie de la vue de votre contrôleur de vue sur votre UITableViewCell

Et utilisez ce code:

MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
  UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
  cell = (MyCustomViewCell *)c.view;
  [c release];
}

Dans votre exemple, en utilisant

[nib objectAtIndex:0]

peut se casser si Apple change l'ordre des éléments dans le xib.

Stephan Burlot
la source
Pour moi, cela se traduit toujours par la création d'une nouvelle instance. dequeue semble revenir à zéro à chaque fois.
étrange
0
 NSString *CellIdentifier = [NSString stringWithFormat:@"cell %ld %ld",(long)indexPath.row,(long)indexPath.section];


    NewsFeedCell *cell = (NewsFeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell=nil;

    if (cell == nil)
    {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:nil options:nil];

        for(id currentObject in topLevelObjects)
        {
            if([currentObject isKindOfClass:[NewsFeedCell class]])
            {
                cell = (NewsFeedCell *)currentObject;
                break;
            }
        }
}
return cell;
2014
la source
0

Cette extension nécessite Xcode7 beta6

extension NSBundle {
    enum LoadViewError: ErrorType {
        case ExpectedXibToExistButGotNil
        case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        case XibReturnedWrongType
    }

    func loadView<T>(name: String) throws -> T {
        let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
        if topLevelObjects == nil {
            throw LoadViewError.ExpectedXibToExistButGotNil
        }
        if topLevelObjects.count != 1 {
            throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        }
        let firstObject: AnyObject! = topLevelObjects.first
        guard let result = firstObject as? T else {
            throw LoadViewError.XibReturnedWrongType
        }
        return result
    }
}

Créez un fichier Xib qui contient seulement 1 UITableViewCell personnalisé.

Charge le.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
neoneye
la source
0
 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cellReuseIdentifier = "collabCell"
            var cell:collabCell! = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? collabCell
            if cell == nil {
                tableView.register(UINib(nibName: "collabCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
                cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! collabCell!
            }


            return cell

}
Hitesh Chauhan
la source