UITableViewCell personalizado de plumín en Swift

139

Estoy tratando de crear una celda de vista de tabla personalizada a partir de una punta. Me refiero a este artículo aquí . Estoy enfrentando dos problemas.

Creé un archivo .xib con un objeto UITableViewCell arrastrado sobre él. Creé una subclase de UITableViewCelly la configuré como la clase de la celda y Cell como el identificador reutilizable.

import UIKit

class CustomOneCell: UITableViewCell {

    @IBOutlet weak var middleLabel: UILabel!
    @IBOutlet weak var leftLabel: UILabel!
    @IBOutlet weak var rightLabel: UILabel!

    required init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

En el UITableViewController tengo este código,

import UIKit

class ViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {

    var items = ["Item 1", "Item2", "Item3", "Item4"]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: - UITableViewDataSource
    override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let identifier = "Cell"
        var cell: CustomOneCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomOneCell
        if cell == nil {
            tableView.registerNib(UINib(nibName: "CustomCellOne", bundle: nil), forCellReuseIdentifier: identifier)
            cell = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomOneCell
        }

        return cell
    }
}

Este código no cumple errores, pero cuando lo ejecuto en el simulador, se ve así.

ingrese la descripción de la imagen aquí

En UITableViewController en el guión gráfico, no he hecho nada a la celda. Identificador en blanco y sin subclase. Intenté agregar el identificador de celda a la celda prototipo y lo ejecuté nuevamente, pero obtengo el mismo resultado.

Otro error que enfrenté es cuando intenté implementar el siguiente método en UITableViewController.

override func tableView(tableView: UITableView!, willDisplayCell cell: CustomOneCell!, forRowAtIndexPath indexPath: NSIndexPath!) {

    cell.middleLabel.text = items[indexPath.row]
    cell.leftLabel.text = items[indexPath.row]
    cell.rightLabel.text = items[indexPath.row]
}

Como se muestra en el artículo que mencioné, cambié la cellforma de tipo del parámetroUITableViewCell al CustomOneCellque es mi subclase de UITableViewCell. Pero me sale el siguiente error,

Método de anulación con el selector 'tableView: willDisplayCell: forRowAtIndexPath:' tiene un tipo incompatible '(UITableView !, CustomOneCell !, NSIndexPath!) -> ()'

¿Alguien tiene alguna idea de cómo resolver estos errores? Estos parecían funcionar bien en Objective-C.

Gracias.

EDITAR: ¡Acabo de notar que si cambio la orientación del simulador al paisaje y lo vuelvo a hacer vertical, aparecen las celdas! Todavía no podía entender qué está pasando. Subí un proyecto de Xcode aquí que demuestra el problema si tienes tiempo para echar un vistazo rápido.

Isuru
fuente

Respuestas:

213

Con Swift 5 y iOS 12.2, debe probar el siguiente código para resolver su problema:

CustomCell.swift

import UIKit

class CustomCell: UITableViewCell {

    // Link those IBOutlets with the UILabels in your .XIB file
    @IBOutlet weak var middleLabel: UILabel!
    @IBOutlet weak var leftLabel: UILabel!
    @IBOutlet weak var rightLabel: UILabel!

}

TableViewController.swift

import UIKit

class TableViewController: UITableViewController {

    let items = ["Item 1", "Item2", "Item3", "Item4"]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
    }

    // MARK: - UITableViewDataSource

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell

        cell.middleLabel.text = items[indexPath.row]
        cell.leftLabel.text = items[indexPath.row]
        cell.rightLabel.text = items[indexPath.row]

        return cell
    }

}

La imagen a continuación muestra un conjunto de restricciones que funcionan con el código proporcionado sin ningún mensaje de ambigüedad de restricciones de Xcode:

ingrese la descripción de la imagen aquí

Imanou Petit
fuente
1
Gracias por la respuesta. Pero eso tampoco funcionó. ¿Necesito cambiar algo en el controlador de vista de tabla? Porque todavía está configurado para prototipo de células.
Isuru
1
Sigue usando células prototipo. Pero asegúrese de haber establecido las buenas restricciones de diseño automático (si usa el diseño automático).
Imanou Petit
1
Subí un proyecto de prueba aquí que demuestra el problema que estoy teniendo. ¿Puedes echarle un vistazo si tienes tiempo?
Isuru
1
Su proyecto de prueba lo confirma: pude hacer que su aplicación funcione bien después de establecer algunas restricciones de diseño automático para su celda personalizada en su archivo .xib. Eche un vistazo a este video si necesita saber más sobre el diseño automático.
Imanou Petit
2
@KirillKudaev no fue renombrado en Swift 4 sino en Swift 3: he arreglado tu edición.
Cœur
30

Este es mi enfoque usando Swift 2 y Xcode 7.3. Este ejemplo utilizará un único ViewController para cargar dos archivos .xib, uno para UITableView y otro para UITableCellView.

ingrese la descripción de la imagen aquí

Para este ejemplo, puede soltar un UITableView directamente en un archivo vacío TableNib .xib. En el interior, configure el propietario del archivo a su clase ViewController y use una salida para hacer referencia a tableView.

ingrese la descripción de la imagen aquí

y

ingrese la descripción de la imagen aquí

Ahora, en su controlador de vista, puede delegar tableView como lo haría normalmente, así

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // Table view delegate
        self.tableView.delegate = self
        self.tableView.dataSource = self

        ...

Para crear su celda personalizada, nuevamente, suelte un objeto Celda de vista de tabla en un archivo vacío TableCellNib .xib. Esta vez, en el archivo .xib de la celda no tiene que especificar un "propietario", pero sí debe especificar una Clase personalizada y un identificador como "TableCellId"

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Crea tu subclase con las salidas que necesites

class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

Finalmente ... de vuelta en su controlador de vista, puede cargar y mostrar todo de esta manera

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    // First load table nib
    let bundle = NSBundle(forClass: self.dynamicType)
    let tableNib = UINib(nibName: "TableNib", bundle: bundle)
    let tableNibView = tableNib.instantiateWithOwner(self, options: nil)[0] as! UIView

    // Then delegate the TableView
    self.tableView.delegate = self
    self.tableView.dataSource = self

    // Set resizable table bounds
    self.tableView.frame = self.view.bounds
    self.tableView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]

    // Register table cell class from nib
    let cellNib = UINib(nibName: "TableCellNib", bundle: bundle)
    self.tableView.registerNib(cellNib, forCellReuseIdentifier: self.tableCellId)

    // Display table with custom cells
    self.view.addSubview(tableNibView)

}

El código muestra cómo puede simplemente cargar y mostrar un archivo plumín (la tabla), y segundo cómo registrar una plumilla para el uso de la celda.

¡¡¡Espero que esto ayude!!!

internet-nico
fuente
2
¿puede explicar cuál es el "tableCellId" en esta línea ... self.tableView.registerNib (cellNib, forCellReuseIdentifier: self.tableCellId) ... porque no ha definido qué es eso. y no puede definir manualmente el identificador en xib .. no hay ninguna opción para definirlo
PRADIP KUMAR
1
En el generador de interfaces, cuando crea el tableCell, en el "inspector de atributos" define un identificador. El mismo identificador es el que usa en su controlador para hacer referencia al objeto. let tableCellId = "myAwesomeCell". Agregué otra imagen para ayudarte.
internet-nico
16

Swift 4

Registrar punta

override func viewDidLoad() {
    super.viewDidLoad()
    tblMissions.register(UINib(nibName: "MissionCell", bundle: nil), forCellReuseIdentifier: "MissionCell")
}

En TableView DataSource

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
          guard let cell = tableView.dequeueReusableCell(withIdentifier: "MissionCell", for: indexPath) as? MissionCell else { return UITableViewCell() }
          return cell
    }
Gurjinder Singh
fuente
9

Solución detallada con capturas de pantalla

  1. Cree un archivo de interfaz de usuario vacío y asígnele un nombre MyCustomCell.xib.

ingrese la descripción de la imagen aquí

  1. Agregue a UITableViewCellcomo la raíz de su archivo xib y cualquier otro componente visual que desee.

ingrese la descripción de la imagen aquí

  1. Cree un archivo de clase de toque de cacao con el nombre de clase MyCustomCellcomo una subclase de UITableViewCell.

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

  1. Establezca la clase personalizada y el identificador de reutilización para su celda de vista de tabla personalizada.

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

  1. Abra el editor asistente y ctrl+dragcree salidas para sus componentes visuales.

ingrese la descripción de la imagen aquí

  1. Configure a UIViewControllerpara usar su celda personalizada.
class MyViewController: UIViewController {

    @IBOutlet weak var myTable: UITableView!

    override func viewDidLoad {
        super.viewDidLoad()

        let nib = UINib(nibName: "MyCustomCell", bundle: nil)
        myTable.register(nib, forCellReuseIdentifier: "MyCustomCell")
        myTable.dataSource = self
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell") as? MyCustomCell {
            cell.myLabel.text = "Hello world."
            return cell
        }
        ...
    }
}
Derek Soike
fuente
Salvaste mi día. Había ... copiado MyHeaderView.swiftpara una celda personalizada. La .swiftvista de cabecera no tiene identifieren Table View Cellen el Attribute Inspector. entonces ... se produjo un error de tiempo de ejecución.
Mazend
Por cierto ... ¿por qué hemos declarado el mismo nombre para el identificador en .swifty en tableView?.register(blahblah, forCellReuseIdentifier: "myCell")? Pensé que uno de ellos no es necesario, pero ... descubrí que ambos son esenciales.
mazend
um ... Tal vez porque ... .xibpara la celda personalizada puede contener múltiples, UITableViewCellentonces ... .xibno es suficiente para ... encontrar la celda correcta.
Mazend
5

No registró su punta como se muestra a continuación:

tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
Sujan
fuente
4

Otro método que puede funcionar para usted (así es como lo hago) es registrar una clase.

Suponga que crea una tableView personalizada como la siguiente:

class UICustomTableViewCell: UITableViewCell {...}

Luego puede registrar esta celda en cualquier UITableViewController en el que la mostrará con "registerClass":

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.registerClass(UICustomTableViewCell.self, forCellReuseIdentifier: "UICustomTableViewCellIdentifier")
}

Y puede llamarlo como esperaría en la celda para el método de fila:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("UICustomTableViewCellIdentifier", forIndexPath: indexPath) as! UICustomTableViewCell
    return cell
}
Ethan Kay
fuente
3

Para corregir el error "Al anular el método ... tiene un tipo incompatible ..." , he cambiado la declaración de la función a

override func tableView(tableView: (UITableView!), 
                        cellForRowAtIndexPath indexPath: (NSIndexPath!)) 
    -> UITableViewCell {...}

(estaba -> UITableViewCell!- con un signo de exclamación al final)

tse
fuente
2

rápido 4.1.2

xib.

Crear ImageCell2.swift

Paso 1

import UIKit

class ImageCell2: UITableViewCell {

    @IBOutlet weak var imgBookLogo: UIImageView!
    @IBOutlet weak var lblTitle: UILabel!
    @IBOutlet weak var lblPublisher: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}

paso 2 . Según la clase Viewcontroller

  import UIKit

    class ImageListVC: UIViewController,UITableViewDataSource,UITableViewDelegate {
    @IBOutlet weak var tblMainVC: UITableView!

    var arrBook : [BookItem] = [BookItem]()

    override func viewDidLoad() {
        super.viewDidLoad()
         //Regester Cell
        self.tblMainVC.register(UINib.init(nibName: "ImageCell2", bundle: nil), forCellReuseIdentifier: "ImageCell2")
        // Response Call adn Disply Record
        APIManagerData._APIManagerInstance.getAPIBook { (itemInstance) in
            self.arrBook = itemInstance.arrItem!
            self.tblMainVC.reloadData()
        }
    }
    //MARK: DataSource & delegate
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.arrBook.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//    [enter image description here][2]
        let cell  = tableView.dequeueReusableCell(withIdentifier: "ImageCell2") as! ImageCell2
        cell.lblTitle.text = self.arrBook[indexPath.row].title
        cell.lblPublisher.text = self.arrBook[indexPath.row].publisher
        if let authors = self.arrBook[indexPath.row].author {
            for item in authors{
                print(" item \(item)")
            }
        }
        let  url  = self.arrBook[indexPath.row].imageURL
        if url == nil {
            cell.imgBookLogo.kf.setImage(with: URL.init(string: ""), placeholder: UIImage.init(named: "download.jpeg"))
        }
        else{
            cell.imgBookLogo.kf.setImage(with: URL(string: url!)!, placeholder: UIImage.init(named: "download.jpeg"))
        }
        return cell
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 90
    } 

}
usuario3263340
fuente
2

Tenía que asegurarme de que al crear la salida especificara que me estaba conectando a la celda, no al propietario del objeto. Cuando el menú aparece para nombrarlo, debe seleccionarlo en el menú desplegable 'objeto'. Por supuesto, también debe declarar la celda como su clase, no solo 'TableViewCellClass'. De lo contrario, seguiría haciendo que la clase no cumpla con las claves.

buckleyJohnson
fuente
1

Simplemente tome un xib con la clase UITableViewCell . Establezca la IU según el requisito y asigne IBOutlet. Úselo en cellForRowAt () de la vista de tabla de esta manera:

//MARK: - table method

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.arrayFruit.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell:simpleTableViewCell? = tableView.dequeueReusableCell(withIdentifier:"simpleTableViewCell") as? simpleTableViewCell
    if cell == nil{
        tableView.register(UINib.init(nibName: "simpleTableViewCell", bundle: nil), forCellReuseIdentifier: "simpleTableViewCell")
        let arrNib:Array = Bundle.main.loadNibNamed("simpleTableViewCell",owner: self, options: nil)!
        cell = arrNib.first as? simpleTableViewCell
    }

    cell?.labelName.text = self.arrayFruit[indexPath.row]
    cell?.imageViewFruit.image = UIImage (named: "fruit_img")

    return cell!

}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
 return 100.0
}

ingrese la descripción de la imagen aquí

100% trabajando sin ningún problema (probado)

Sr. Javed Multani
fuente
dice que la celda de controles? .labelName es nulo
Vignesh