web-dev-qa-db-fra.com

Comment charger des UITableViewCells personnalisées à partir de fichiers Xib?

La question est simple: comment charger une variable UITableViewCell personnalisée à 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 de discussion mentionne le problème et suggère une solution, mais il s'agit d'une version antérieure à la publication de la NDA et manquant de code. Voici un fil long qui traite du problème sans fournir de réponse définitive. 

Voici du 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 UITableViewCell et ajoutez IBOutlets pour les composants souhaités. Créez ensuite un nouveau fichier "XIB vide". Ouvrez le fichier Xib dans IB, ajoutez un objet UITableViewCell, définissez son identificateur sur "MyCellIdentifier", définissez sa classe sur MyCell et ajoutez vos composants. Enfin, connectez la IBOutlets aux 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 d'avertir les fuites de mémoire si le fichier Xib n'est pas chargé via une classe d'usine supplémentaire. J'ai testé ce qui précède sous Instruments/Fuites et je n'ai constaté aucune fuite de mémoire. 

Alors, quel est le moyen canonique de charger des cellules à partir de Xibs? Définissons-nous le propriétaire du fichier? Avons-nous besoin d'une usine? Si tel est le cas, à quoi ressemble le code de l'usine? S'il existe plusieurs solutions, clarifions le pour et le contre de chacune d'entre elles ...

284
DrGary

Voici deux méthodes pour lesquelles l'auteur original a été recommandé par un ingénieur de l'IB .

Voir le post actuel pour plus de détails. Je préfère la méthode n ° 2 car cela 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;
}

Update (2014): La méthode n ° 2 est toujours valide mais il n'y a plus de documentation à ce sujet. Il était jadis dans la documentation officielle mais est maintenant supprimé au profit des storyboards.

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

287
bentford

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;
}
299
giuseppe

Registre

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 réalisable en créant les cellules dans les fichiers .xib ou .stroyboard, en tant que cellules prototypes . Si vous devez y attacher une classe, vous pouvez sélectionner le prototype de cellule et ajouter la classe correspondante (il faut bien entendu être un descendant de UITableViewCell).

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 est que cette nouvelle méthode non seulement met la cellule en file d'attente, mais qu'elle est également inexistante (cela signifie que vous n'avez pas à faire if (cell == nil) shenanigans) et que la cellule est prête à être utilisée comme dans l'exemple ci-dessus.

( Warning ) tableView.dequeueReusableCell(withIdentifier:for:) a le nouveau comportement. Si vous appelez l’autre (sans indexPath:), vous obtenez l’ancien comportement dans lequel vous devez rechercher nil et l’instruire vous-même, notez la valeur renvoyée UITableViewCell?.

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 à la cellule est celui que vous avez défini dans le fichier .xib pour la sous-classe UITableViewCell 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 du contenu (telles que les étiquettes et les vues d’image) au moment où vous les avez enregistrées, et vous les remplissez simplement avec la méthode cellForRowAtIndexPath.

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.

38
Can

A pris la réponse de Shawn Craver et nettoyé 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 mes UITableViewCell, puis je remplace la norme

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

avec:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
33
vilcsak

J'ai utilisé bentford's 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;
}

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

En passant owner:self dans votre instruction loadNibNamed, vous définissez UITableViewController en tant que propriétaire du fichier de votre UITableViewCell.

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

Dans loadNibNamed:owner:options, le code d'Apple essaiera de définir les propriétés de votre UITableViewController, puisque c'est le propriétaire. Mais ces propriétés ne sont pas définies ici. Vous obtenez donc une erreur indiquant que vous êtes conforme à la codification key value:

*** 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 sur la variable UITableViewCell au lieu de Propriétaire du fichier:

  1. Faites un clic droit sur le propriétaire du fichier pour afficher la liste des connexions.
  2. Prenez une capture d'écran avec Commande-Maj-4 (faites glisser pour sélectionner la zone à capturer)
  3. x sur les connexions du propriétaire du fichier
  4. Cliquez avec le bouton droit sur UITableCell dans la hiérarchie des objets et rajoutez les connexions.
16
funroll

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 le moyen le plus concis que j'ai trouvé.

1. Construisez votre Xib dans Interface Builder à votre guise

  • 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 à la date de cette écriture), utilisez simplement une UIView de l'interface dans Xcode 4 si vous l'avez toujours
  • Disposez vos vues secondaires dans cette cellule et attachez vos connexions IBOutlet à votre @interface au format .h ou .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;
}
13
webstersx

Si vous utilisez Interface Builder pour créer des cellules, vérifiez que vous avez défini l'identifiant dans l'inspecteur. Ensuite, vérifiez qu'il en va de même lorsque vous appelez dequeueReusableCellWithIdentifier.

J'ai accidentellement oublié de définir des identifiants dans un projet contenant beaucoup de tables et le changement de performances ressemblait à un jour et une nuit.

9
Alex R. Young

Charger UITableViewCells à partir de XIBs é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 d'UIViews qui en est la cause).

Je vous suggère de jeter un coup d'oeil à ceci: Référence du lien

8
Can Berk Güder

Voici la méthode de classe que j'utilise 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 fichier XIB, j'ai défini le nom de la classe et l'identificateur de réutilisation. Après cela, je peux simplement appeler cette méthode dans mon contrôleur de vue au lieu de la

[[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 du moins dans mon esprit, plus fiable que l'exemple de Stephan Burlot, car vous êtes assuré de n'obtenir que la vue d'un fichier XIB du bon type.

6
Shawn Craver

La solution correcte est-ce

- (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; 
    }
5
Hamiz Ahmed

Vérifiez ceci - http://eppz.eu/blog/custom-uitableview-cell/ - de manière vraiment pratique en utilisant une petite classe qui aboutit à une ligne dans l'implémentation du contrôleur:

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

enter image description here

4
Geri

Recharger la carte 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., au nib, même à plusieurs cellules, en utilisant cette méthode (le "registerNIB" d’Apple ne permet qu’un seul objet de niveau supérieur - le bogue 10580062 ". IOS5 tableView registerNib: trop restrictif"

Donc mon code est en dessous - vous lisez une fois dans le NIB (en initialisant comme moi ou dans viewDidload - peu importe. Ensuite, vous instanciez le nib en objets puis en sélectionnant celui dont vous avez besoin. C'est beaucoup plus efficace que de charger le 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;
}
4
David H

La bonne façon de le faire est de créer une implémentation de sous-classe UITableViewCell, un en-tête et XIB. Dans 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, indiquez le nom de la classe de la sous-classe UITableViewController. Connectez le propriétaire du fichier à la cellule à l'aide de la sortie tableViewView.

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;
}
3

Pour ce faire, je déclare un IBOutlet UITableViewCell *cell dans votre classe de contrôleur . Ensuite, appelez la méthode de la classe NSBundle loadNibNamed, qui alimentera la UITableViewCell dans la cellule déclarée ci-dessus.

Pour xib, je vais créer un xib vide et ajouter l'objet UITableViewCell dans IB où il peut être configuré selon les besoins. Cette vue est ensuite connectée à la cellule IBOutlet dans 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;
}

NSBundle additions loadNibNamed (identifiant ADC)

Article de cocoawithlove.com dont je me suis inspiré du concept (obtenez l'exemple d'application des numéros de téléphone)

3
Ryan Townshend

Commencez par importer votre fichier de cellule personnalisé #import "CustomCell.h", puis modifiez la méthode de délégué 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;
}
2
Mohit

Voici une approche universelle pour l'enregistrement de 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. Le protocole Reusable 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. UITableViewCell est conforme au protocole Reusable.
  3. L'extension UITableView résume la différence d'enregistrement de cellules via nib ou class.

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
}
1
Vadim Bulavin

Dans Swift 4.2 et Xcode 10

J'ai trois fichiers de cellules XIB

dans ViewDidLoad, enregistrez vos fichiers XIB comme ceci ...

C'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:

Ce sont 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
    }

}
1
iOS

Voici ma méthode pour cela: Chargement de UITableViewCells personnalisés à partir de fichiers XIB… Encore une autre méthode

L'idée est de créer une sous-classe SampleCell de UITableViewCell avec une propriété IBOutlet UIView *content et une propriété pour chaque sous-vue personnalisée à configurer à partir du code. Ensuite, créez un fichier SampleCell.xib. Dans ce fichier nib, changez le propriétaire du fichier en SampleCell. Ajoutez un contenu UIView de taille adaptée à vos besoins. Ajoutez et configurez toutes les sous-vues (étiquette, vues d'image, boutons, etc.) souhaitées. Enfin, liez la vue du contenu et les sous-vues au propriétaire du fichier.

1
MonsieurDart

Je ne sais pas s'il existe un moyen 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 une UITableViewCell
  • Définissez la classe de votre UITableViewCell sur votre classe personnalisée
  • Définir l'identifiant de votre UITableViewCell
  • Définissez la sortie de votre vue de 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 rompre si Apple modifie l'ordre des éléments dans la xib.

0
Stephan Burlot
 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

}
0
Hitesh Chauhan

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 contenant seulement 1 UITableViewCell personnalisé.

Charge le.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
0
neoneye
 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;
0
2014