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

La pregunta es simple: ¿cómo se carga UITableViewCell personalizado desde 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 un lanzamiento previo a NDA y carece de código. Aquí hay un largo hilo que discute el problema sin dar 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” y establezca su clase en MyCell y agregue sus componentes. Finalmente, conecte los IBOutlets a 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 de memory leaks 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 células de Xibs? ¿Establecemos el propietario del archivo? ¿Necesitamos una fábrica? Si es así, ¿cómo es el código para la fábrica? Si hay múltiples soluciones, aclaremos los pros y los contras de cada una de ellas …

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

Vea la publicación actual para más detalles. Prefiero el método n. ° 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 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 (presumbly the custom cell, as that's all the XIB should contain). cell = [topLevelObjects objectAtIndex:0]; } return cell; } 

Actualización (2014): el método n. ° 2 sigue siendo válido, pero ya no hay documentación para él. 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

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

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 adjuntar una clase a ellos, puede seleccionar el prototipo de celda y agregar la clase correspondiente (debe ser un descendiente de UITableViewCell , por supuesto).

Dequeue

Y luego, dequeued usando ( 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 dequeue la celda, sino que también crea si no existe (eso significa que no tienes que hacer if (cell == nil) shenanigans), y la célula está lista para usar igual que 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 necesita comprobar nil e 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 la que definió en el archivo .xib para la subclase UITableViewCell 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 de contenido (como tags y vistas de imágenes) para el momento en que las registró, y en el método cellForRowAtIndexPath simplemente las rellena.

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.

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

BBCell.h:

 #import  @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 BBableell de UITableViewCell, y luego reemplazo 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 (presumbly the custom cell, as that's all the XIB should contain). cell = [topLevelObjects objectAtIndex:0]; } return cell; } 

Funciona, pero ten cuidado con las conexiones al propietario del archivo en tu archivo personalizado .jpg de UITableViewCell.

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

Si arrastra y suelta al 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 encoding de valor clave :

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

Si un evento se activa 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 sus conexiones de Interface Builder a UITableViewCell lugar de al propietario del archivo:

  1. Haga clic derecho en el propietario del archivo para ver la lista de conexiones
  2. Tome una captura de pantalla con Command-Shift-4 (arrastre para seleccionar el área que se capturará)
  3. x las conexiones del propietario del archivo
  4. Haga clic derecho en UITableCell en la jerarquía de objetos y vuelva a agregar las conexiones.

Decidí publicar porque no me gusta ninguna de estas respuestas; las cosas siempre pueden ser más simples y esta es, de lejos, la forma más concisa que he encontrado.

1. Crea tu Xib en Interface Builder como te gusta

  • Establezca el propietario del archivo en la clase NSObject
  • Agregue una UITableViewCell y configure su clase en MyTableViewCellSubclass: si su IB se bloquea (ocurre en Xcode> 4 en el momento de redactar este documento), simplemente use una UIView de la interfaz en Xcode 4 si aún la tiene extendida.
  • Diseña tus subvistas dentro de esta celda y adjunta tus conexiones de IBOutlet a tu @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; } 

Si está utilizando Interface Builder para crear celdas, verifique que haya configurado el Identificador en el Inspector. Luego, compruebe que es lo mismo cuando llama a dequeueReusableCellWithIdentifier.

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

Cargar UITableViewCells desde XIB ahorra una gran cantidad de código, pero generalmente da como resultado una velocidad de desplazamiento horrible (en realidad, no es el XIB sino el uso excesivo de UIViews el que causa esto).

Te sugiero que le eches un vistazo a esto: referencia del enlace

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 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 a [nib objectAtIndex:0] y, en mi opinión, al menos, es más confiable que el ejemplo de Stephan Burlot porque se garantiza que solo obtendrá una vista de un XIB que sea del tipo correcto.

La solución correcta es esto

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

Recargar el NIB es costoso. 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, usando este método (el “registerNIB” iOS5 de Apple solo permite un objeto de nivel superior – Error 10580062 “iOS5 tableView registerNib: demasiado restrictivo”

Así que mi código está abajo: leyó en el NIB una vez (en inicializar como lo hice o en viewDidload – whatever. A partir de ahí, crea una instancia del objeto en objetos y luego selecciona el que necesita. Esto es mucho más eficiente que cargar el plumín 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; } 

Comprueba esto – http://eppz.eu/blog/custom-uitableview-cell/ – manera muy práctica usando una pequeña clase 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]; } 

enter image description here

La forma correcta de hacerlo es crear una implementación de subclase UITableViewCell, encabezado y XIB. 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 la 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; } 

Lo que hago para esto es declarar una IBOutlet UITableViewCell *cell en su clase de controlador. A continuación, 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 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 agrega loadNibNamed (inicio de sesión de ADC)

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

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; } 
  1. Cree su propia clase personalizada Subclase AbcViewCell desde UITableViewCell (Asegúrese de que su nombre de archivo de clase y el nombre de archivo de punta son los mismos)

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

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

    let cell: AbcViewCell = UITableViewCell.fromNib()

No sé si hay una manera canónica, pero este es mi método:

  • Crea un xib para un ViewController
  • Establezca la clase Propietario de archivo en UIViewController
  • Eliminar la vista y agregar una UITableViewCell
  • Establezca la clase de su UITableViewCell en su clase personalizada
  • Establezca el Identificador de su UITableViewCell
  • Establezca la salida de su vista del 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 tu ejemplo, usando

 [nib objectAtIndex:0] 

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

Aquí está mi método para eso: Cargando UITableViewCells personalizadas desde archivos XIB … Sin embargo, otro método

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 desde el código. Luego, para crear un archivo SampleCell.xib. En este archivo de punta, cambie el propietario del archivo a SampleCell. Agregue un contenido UIView dimensionado para satisfacer sus necesidades. Agregue y configure todas las subvistas (tags, vistas de imágenes, botones, etc.) que desee. Finalmente, vincule la vista de contenido y las subvistas al propietario del archivo.

  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; 

Esta extensión requiere Xcode7 beta6

 extension NSBundle { enum LoadViewError: ErrorType { case ExpectedXibToExistButGotNil case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects case XibReturnedWrongType } func loadView(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") 

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(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 la ID de la celda a partir de su nombre de clase. Asegúrese de seguir la convención: cell ID == class name == nib name .
  2. UITableViewCell cumple con el protocolo Reusable .
  3. UITableView extensión UITableView abstrae la diferencia en el registro de celdas mediante plumín 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 } 
  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 }