ios uitableview cocoa-touch xib

ios - ¿Cómo cargar UITableViewCells personalizados desde archivos Xib?



cocoa-touch (23)

La pregunta es simple: ¿Cómo cargar UITableViewCell personalizado desde archivos Xib? Si lo hace, le permite utilizar Interface Builder para diseñar sus celdas. Al parecer, la respuesta no es simple debido a problemas de gestión de memoria. Este hilo menciona el problema y sugiere una solución, pero es anterior a la versión de NDA y carece de código. Aquí hay un largo hilo que trata el tema 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 UITableViewCell y agregue IBOutlets para los componentes que desee. Luego crea un nuevo archivo "Empty XIB". Abra el archivo Xib en IB, agregue un objeto UITableViewCell , establezca su identificador en "MyCellIdentifier", establezca su clase en MyCell y agregue sus componentes. Finalmente, conecte los IBOutlets a los componentes. Tenga en cuenta que no establecimos el propietario del archivo en IB.

Otros métodos recomiendan establecer el propietario del archivo y advertir sobre las 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 ninguna pérdida 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í, ¿cuál es el código para la fábrica? Si hay varias soluciones, aclaremos los pros y los contras de cada una de ellas ...


Registro

Después de iOS 7, este proceso se ha simplificado hasta ( 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 .xib o .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 retiró el uso de ( 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 elimina la cola de la celda, sino que también crea si no existe (lo que significa que no tiene que hacer if (cell == nil) shenanigans), 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 comprobar si es nil y lo UITableViewCell? 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 la que definió en el archivo .xib para la subclase UITableViewCell , o alternativamente, utilizando el otro método de registro.

Configuración

Lo ideal es que sus celdas ya hayan sido configuradas en términos de apariencia y posicionamiento del contenido (como etiquetas y vistas de imagen) en el momento en que las registró, y en el método cellForRowAtIndexPath simplemente las llena.

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.


  1. Cree su propia subclase AbcViewCell clase personalizada desde UITableViewCell (asegúrese de que su nombre de archivo de clase y su nombre de archivo de punta sean iguales)

  2. Crear 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. Utilízalo

    let cell: AbcViewCell = UITableViewCell.fromNib()


Aquí está el método de clase que he estado usando para crear celdas personalizadas 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, establezco el nombre de la clase y reutilizo el identificador. Después de eso, solo 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 a [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 sea del tipo correcto.


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

La idea es crear una subclase SampleCell de UITableViewCell con una propiedad de IBOutlet UIView *content y una propiedad para cada subvista personalizada que necesite configurar a partir del código. A continuación, para crear un archivo SampleCell.xib. En este archivo de plumilla, cambie el propietario del archivo a SampleCell. Agregue un contenido de tamaño UIView para adaptarse a 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.


Aquí hay dos métodos que el autor original afirma que fueron recomendados por un ingeniero de IB .

Ver 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 en favor de los guiones gráficos.

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


Aquí hay un enfoque universal para registrar celdas 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. Reusable protocolo Reusable genera el ID de celda a partir de su nombre de clase. Asegúrese de seguir la convención: cell ID == class name == nib name .
  2. UITableViewCell ajusta al protocolo Reusable .
  3. UITableView extensión UITableView abstrae la diferencia en el registro de celdas mediante nib o clase.

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 }


Cargar UITableViewCells desde XIBs guarda una gran cantidad de 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).

Te sugiero que eches un vistazo a esto: enlace de referencia


Decidí publicar porque no me gustan ninguna de estas respuestas: las cosas siempre pueden ser más simples y esta es 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 falla (ocurre en Xcode> 4 en el momento de esta escritura), solo use una vista UIV de la interfaz en Xcode 4 si todavía lo tiene por ahí
  • Diseñe sus subvistas dentro de esta celda y adjunte sus conexiones de IBOutlet a su @interfaz en la .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 tu MyTableViewCellSubclass

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


En Swift 4.2 y Xcode 10

Tengo tres archivos de celdas XIB

en ViewDidLoad registra tus 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 de delegado 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 } }


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


La forma correcta de hacerlo es crear una subclase, un encabezado y XIB de UITableViewCell. En el XIB, elimine cualquier vista y solo 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 mediante la salida tableViewCell.

En el archivo de cabecera:

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


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


La solución correcta es la siguiente:

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


Lo que hago para esto es declarar una IBOutlet UITableViewCell *cell en su clase de controlador. Luego invoque el método de clase NSBundle loadNibNamed , que alimentará el UITableViewCell a la celda declarada anteriormente.

Para el xib, crearé un xib vacío y UITableViewCell objeto UITableViewCell en IB, donde se puede configurar según sea necesario. Esta vista luego se conecta a la celda IBOutlet en 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 additions loadNibNamed (inicio de sesión de ADC)

Artículo de cocoawithlove.com del que obtuve el concepto (obtenga la aplicación de muestra de números de teléfono)


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

  • Crear un xib para un ViewController
  • Establezca la clase de propietario de archivo en UIViewController
  • Eliminar la vista y agregar un UITableViewCell
  • Establezca la Clase de su UITableViewCell en su clase personalizada
  • Establece el identificador de tu UITableViewCell
  • Establezca la salida de la vista del controlador de vista en 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 tu ejemplo, usando

[nib objectAtIndex:0]

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


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


Recargar el NIB es caro. Es mejor cargarlo una vez, luego crear una instancia de los objetos cuando necesite una celda. Tenga en cuenta que puede agregar UIImageViews, etc., a la punta, incluso a 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"

Por lo tanto, mi código está abajo: leyó en el NIB una vez (al inicializar como lo hice o en viewDidload, lo que sea. Desde ese momento, crea una instancia de la pluma en los objetos y luego elige el que necesita. Esto es mucho más eficiente que cargar la pluma. 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; }


Si está utilizando Interface Builder para crear celdas, verifique que haya establecido el Identificador en el Inspector. Luego verifique que sea lo mismo cuando llame a dequeueReusableCellWithIdentifier.

Accidentalmente, me olvidé de configurar algunos identificadores en un proyecto de mesa pesada, y el cambio de rendimiento fue como la noche y el día.


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

Realizo todas las subclases de BBCell de mi UITableViewCell y luego sustituyo el estándar.

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

con:

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


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 tenga cuidado con las conexiones al propietario del archivo en su archivo personalizado .xib UITableViewCell.

Al pasar owner:self en su declaración loadNibNamed , establece el UITableViewController como el propietario del archivo de su UITableViewCell .

Si arrastra y suelta el archivo del encabezado en IB para configurar acciones y salidas, se 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 recibe un error sobre 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ás 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 sencilla es apuntar sus conexiones de Interface Builder a la UITableViewCell lugar del propietario del archivo:

  1. Haga clic derecho en el propietario del archivo para abrir la lista de conexiones.
  2. Realice 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 en el UITableCell en la jerarquía de objetos y vuelva a agregar las conexiones.

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

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


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;


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 }