¿Cómo se cargan UITableViewCells personalizadas desde archivos Xib?

293

La pregunta es simple: ¿cómo se carga personalizado UITableViewCelldesde archivos Xib? Hacerlo le permite usar Interface Builder para diseñar sus celdas. La respuesta aparentemente no es simple debido a problemas de administración de memoria. Este hilo menciona el problema y sugiere una solución, pero es anterior al lanzamiento de NDA y carece de código. Aquí hay un largo hilo que discute el problema sin proporcionar una respuesta definitiva.

Aquí hay un código que he usado:

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

Para usar este código, cree MyCell.m / .h, una nueva subclase de UITableViewCelly agregue IBOutletslos componentes que desee. Luego cree un nuevo archivo "XIB vacío". Abra el archivo Xib en IB, agregue un UITableViewCellobjeto, establezca su identificador en "MyCellIdentifier", y establezca su clase en MyCell y agregue sus componentes. Finalmente, conecte el IBOutletsa los componentes. Tenga en cuenta que no configuramos el propietario del archivo en IB.

Otros métodos recomiendan configurar el propietario del archivo y advertir sobre pérdidas de memoria si el Xib no se carga a través de una clase de fábrica adicional. Probé lo anterior en Instrumentos / Fugas y no vi pérdidas de memoria.

Entonces, ¿cuál es la forma canónica de cargar celdas desde Xibs? ¿Configuramos el propietario del archivo? ¿Necesitamos una fábrica? Si es así, ¿cómo se ve el código para la fábrica? Si hay varias soluciones, aclaremos los pros y los contras de cada una de ellas ...

DrGary
fuente
2
¿Alguien puede editar el tema para hacer realmente la pregunta, es decir, "¿Cómo se cargan UITableViewCells personalizados desde archivos Xib?" (Ignore si esto simplemente no es posible en stackoverflow.)
Steven Fisher
1
Para iOS 5 y más allá, esta es la solución: stackoverflow.com/questions/15591364/… , que es lo mismo que la solución de giuseppe.
Matt Becker
Nota rápida, más simple (entorno 2013) responde aquí stackoverflow.com/questions/15378788/… jamihash
Fattie

Respuestas:

288

Aquí hay dos métodos que, según el autor original, fueron recomendados por un ingeniero de IB .

Vea la publicación real para más detalles. Prefiero el método # 2 ya que parece más simple.

Método 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étodo 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;
}

Actualización (2014): el Método # 2 sigue siendo válido pero ya no hay documentación para ello. Solía ​​estar en los documentos oficiales, pero ahora se elimina a favor de los guiones gráficos.

Publiqué un ejemplo de trabajo en Github:
https://github.com/bentford/NibTableCellExample

editar para 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
fuente
1
Para el método 1, ¿no debería hacer algo como "cell = (BDCustomCell *) [[temporaryController.view retiene] autorelease];" ¿Entonces la célula no se libera cuando se libera el controlador temporal?
Tod Cunningham
Hm. La documentación que habla sobre el n. ° 2 todavía le dice que configure el propietario de la celda en el archivo XIB, a una clase de controlador conocida. Tal vez no importe cuando configura el propietario durante la carga.
Oscar
@OscarGoldman El propietario de la celda en el archivo XIB es una clase (es decir, el tipo de propietario). El propietario de la celda en loadNibNamed: owner: options: es un objeto del tipo especificado en XIB.
bentford
2
@CoolDocMan Opción # 2 todavía funciona. El problema es más probable con la punta. Aquí hay un ejemplo: github.com/bentford/NibTableCellExample
bentford
2
¿Por qué este código súper antiguo se clasifica tan alto? Stackoverflow hace algo: /
Nico S.
304

La solución correcta es esta:

- (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
fuente
¿Eso va a romper las aplicaciones iOS5? Realmente nunca he visto UINib
Adam Waite
@AdamWaite El registro de archivos NIB funciona para iOS 5 y posterior, por lo que no está rompiendo las aplicaciones de iOS 5. Y UINib incluso existe desde iOS 4.
Mecki
Para un buen ejemplo, consulte el repositorio de git al que se hace referencia en la respuesta superior aquí: stackoverflow.com/questions/18746929/…
netigger
39

Registrarse

Después de iOS 7, este proceso se ha simplificado a ( 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")

( Nota ) Esto también se puede lograr creando las celdas en los archivos .xibo .stroyboard, como celdas prototipo. Si necesita adjuntarles una clase, puede seleccionar el prototipo de celda y agregar la clase correspondiente (debe ser un descendiente de UITableViewCell, por supuesto).

Dequeue

Y más tarde, se dejó de usar ( 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 diferencia es que este nuevo método no solo quita la celula, sino que también crea si no existe (eso significa que no tiene que hacer if (cell == nil)travesuras), y la celda está lista para usar como en el ejemplo anterior.

( Advertencia ) tableView.dequeueReusableCell(withIdentifier:for:)tiene el nuevo comportamiento, si llama al otro (sin indexPath:) obtiene el comportamiento anterior, en el que debe verificarlo nile instanciarlo usted mismo, observe el UITableViewCell?valor de retorno.

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

Y, por supuesto, el tipo de la clase asociada de la celda es el que usted definió en el archivo .xib para la UITableViewCellsubclase, o alternativamente, utilizando el otro método de registro.

Configuración

Idealmente, sus celdas ya se han configurado en términos de apariencia y posicionamiento del contenido (como etiquetas y vistas de imágenes) cuando las registró, y en el cellForRowAtIndexPathmétodo simplemente las completa.

Todos juntos

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

Y, por supuesto, todo esto está disponible en ObjC con los mismos nombres.

Poder
fuente
Aquí está la versión objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb
33

Tomó la respuesta de Shawn Craver y la limpió un poco.

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

Hago todas las subclases de BBCableViewCell de BBCell, y luego reemplazo el estándar

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

con:

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

Usé el Método # 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;
}

Funciona, pero ten cuidado con las conexiones con el propietario del archivo en tu archivo UITableViewCell .xib personalizado.

Al pasar owner:selfsu loadNibNamedestado de cuenta, establece el UITableViewControllercomo propietario del archivo de su UITableViewCell.

Si arrastra y suelta el archivo de encabezado en IB para configurar acciones y puntos de venta, los configurará como Propietario del archivo de forma predeterminada.

En loadNibNamed:owner:options, el código de Apple intentará establecer propiedades en su UITableViewController, ya que ese es el propietario. Pero no tiene esas propiedades definidas allí, por lo que obtiene un error acerca de ser compatible con la codificación de valores clave :

*** 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 se activa un Evento en su lugar, obtendrá una 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

Una solución fácil es apuntar las conexiones de Interface Builder hacia el UITableViewCellpropietario del archivo en lugar del:

  1. Haga clic derecho en el Propietario del archivo para abrir la lista de conexiones
  2. Tome una captura de pantalla con Command-Shift-4 (arrastre para seleccionar el área a capturar)
  3. x fuera de las conexiones del propietario del archivo
  4. Haga clic derecho sobre UITableCell en la jerarquía de objetos y vuelva a agregar las conexiones.
funroll
fuente
Tuve el problema que mencionaste, pero ¿cómo señalar las conexiones a UITableViewCell en lugar del propietario del archivo? No entiendo tus pasos, por ejemplo, ¿por qué es necesario tomar una captura de pantalla? y cuando hice clic en el botón Agregar al lado de la salida, no pasa nada
xu huanze
@xuhuanze Sugerí tomar una captura de pantalla para que tengas un registro de las cosas a las que el propietario del archivo ya estaba conectado. Entonces puedes volver a crear esas mismas conexiones. Debe arrastrar y soltar para agregar las conexiones, no solo un clic.
funroll
Muchas gracias, tuve el problema "esta clase no cumple con la codificación del valor clave para la clave" y lo resolví con su ayuda. Quiero decirles a los demás, que también deben cambiar una clase de su UITableViewCell a su clase, que usan como una clase de celda personalizada.
Denis Kutlubaev
14

He decidido publicar porque no me gusta ninguna de estas respuestas: las cosas siempre pueden ser más simples y esta es, con mucho, la forma más concisa que he encontrado.

1. Construye tu Xib en Interface Builder como quieras

  • Establecer el propietario del archivo a la clase NSObject
  • Agregue un UITableViewCell y establezca su clase en MyTableViewCellSubclass: si su IB se bloquea (ocurre en Xcode> 4 al momento de escribir esto), solo use una UIView de hacer la interfaz en Xcode 4 si todavía lo tiene tendido
  • Diseñe sus subvistas dentro de esta celda y adjunte sus conexiones IBOutlet a su @interface en .h o .m (.m es mi preferencia)

2. En su subclase UIViewController o 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. En su MyTableViewCellSubclass

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

    return self;
}
webstersx
fuente
9

Si usa Interface Builder para crear celdas, verifique que haya configurado el Identificador en el Inspector. Luego verifique que sea lo mismo cuando llame a dequeueReusableCellWithIdentifier.

Accidentalmente olvidé establecer algunos identificadores en un proyecto pesado, y el cambio de rendimiento fue como la noche y el día.

Alex R. Young
fuente
8

Cargar UITableViewCells desde XIB ahorra mucho código, pero generalmente resulta en una velocidad de desplazamiento horrible (en realidad, no es el XIB sino el uso excesivo de UIViews lo que causa esto).

Le sugiero que eche un vistazo a esto: Referencia de enlace

Can Berk Güder
fuente
6

Este es el método de clase que he estado usando para crear celdas personalizadas a 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;
}

Luego, en el XIB, configuro el nombre de la clase y reutilizo el identificador. Después de eso, puedo llamar a ese método en mi controlador de vista en lugar de

[[UITableViewCell] alloc] initWithFrame:]

Es lo suficientemente rápido y se usa en dos de mis aplicaciones de envío. Es más confiable que llamar [nib objectAtIndex:0], y en mi opinión al menos, más confiable que el ejemplo de Stephan Burlot porque está garantizado que solo obtendrá una vista de un XIB que es el tipo correcto.

Shawn Craver
fuente
5

La solución correcta es esta

- (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
fuente
4

Recargar la NIB es costoso. Es mejor cargarlo una vez, luego instanciar los objetos cuando necesite una celda. Tenga en cuenta que puede agregar UIImageViews, etc. a la plumilla, incluso varias celdas, utilizando este método (el "registerNIB" de Apple iOS5 solo permite un objeto de nivel superior: error 10580062 "iOS5 tableView registerNib: demasiado restrictivo"

Entonces, mi código está debajo: lees en la NIB una vez (en inicializar como lo hice o en viewDidload, lo que sea. A partir de entonces, instancias la punta en objetos y luego eliges la que necesitas. Esto es mucho más eficiente que cargar la punta una y otra vez.

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
fuente
4

Verifique esto - http://eppz.eu/blog/custom-uitableview-cell/ - forma realmente conveniente utilizando una clase pequeña que termina una línea en la implementación del controlador:

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

ingrese la descripción de la imagen aquí

Geri Borbás
fuente
3

La forma correcta de hacerlo es crear una implementación, encabezado y XIB de la subclase UITableViewCell. En el XIB, elimine cualquier vista y simplemente agregue una celda de tabla. Establezca la clase como el nombre de la subclase UITableViewCell. Para el propietario del archivo, conviértalo en el nombre de clase de subclase UITableViewController. Conecte el propietario del archivo a la celda utilizando la salida tableViewCell.

En el archivo de encabezado:

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

En el archivo de implementación:

@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
fuente
3

Lo que hago para esto es declarar un IBOutlet UITableViewCell *cellen su clase de controlador. Luego invoque el NSBundle loadNibNamedmétodo de clase, que alimentará UITableViewCella la celda declarada anteriormente.

Para el xib, crearé un xib vacío y agregaré el UITableViewCellobjeto en IB donde se puede configurar según sea necesario. Esta vista se conecta a la celda IBOutleten la clase de controlador.

- (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 adiciones loadNibNamed (inicio de sesión ADC)

Artículo de cocoawithlove.com de donde obtuve el concepto

Ryan Townshend
fuente
3
  1. Cree su propia AbcViewCellsubclase de clase personalizada a partir de UITableViewCell(Asegúrese de que el nombre del archivo de clase y el nombre del archivo de punta sean los mismos)

  2. Cree este método de clase de extensión.

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

    let cell: AbcViewCell = UITableViewCell.fromNib()

William Hu
fuente
2

Primero importe su archivo de celda personalizado #import "CustomCell.h"y luego cambie el método de delegado como se menciona a continuación:

- (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
fuente
2

En Swift 4.2 y Xcode 10

Tengo tres archivos de celda XIB

en ViewDidLoad registre sus archivos XIB como este ...

Este es el primer acercamiento

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")

El segundo enfoque registra directamente los archivos XIB en cellForRowAt indexPath:

Estas son mis funciones delegadas de 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
fuente
1

Aquí está mi método para eso: cargar UITableViewCells personalizadas desde archivos XIB ... Otro método más

La idea es crear una subclase SampleCell de UITableViewCellcon una IBOutlet UIView *contentpropiedad y una propiedad para cada subvista personalizada que necesita configurar desde el código. Luego, para crear un archivo SampleCell.xib. En este archivo plumín, cambie el propietario del archivo a SampleCell. Agregue un contenido UIViewdimensionado para satisfacer sus necesidades. Agregue y configure todas las subvistas (etiquetas, vistas de imágenes, botones, etc.) que desee. Finalmente, vincule la vista de contenido y las subvistas al propietario del archivo.

MonsieurDart
fuente
1

Aquí hay un enfoque universal para registrar células en 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)
    }
}

Explicación:

  1. ReusableEl protocolo genera ID de celda a partir de su nombre de clase. Asegúrese de seguir la convención: cell ID == class name == nib name.
  2. UITableViewCellse ajusta al Reusableprotocolo
  3. UITableView la extensión abstrae la diferencia en el registro de celdas a través de nib o class.

Ejemplo de uso:

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
fuente
0

No sé si hay una forma canónica, pero aquí está mi método:

  • Crear un xib para un ViewController
  • Establezca la clase Propietario de archivo en UIViewController
  • Eliminar la vista y agregar un UITableViewCell
  • Establezca la clase de su UITableViewCell a su clase personalizada
  • Establecer el identificador de su UITableViewCell
  • Establezca la salida de su vista de controlador de vista a su UITableViewCell

Y usa este código:

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

En su ejemplo, usando

[nib objectAtIndex:0]

puede romperse si Apple cambia el orden de los elementos en el xib.

Stephan Burlot
fuente
Para mí, esto resulta en crear una nueva instancia siempre. la cola parece estar volviendo nula cada vez.
extraño
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
fuente
0

Esta extensión requiere 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
    }
}

Cree un archivo Xib que contenga solo 1 UITableViewCell personalizado.

Cárgalo.

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