UITableViewCell personalizzato dal pennino in Swift


139

Sto cercando di creare una cella di visualizzazione tabella personalizzata da un pennino. Mi riferisco a questo articolo qui . Sto affrontando due problemi.

Ho creato un file .xib con un oggetto UITableViewCell trascinato su di esso. Ho creato una sottoclasse UITableViewCelle l'ho impostata come classe della cella e Cell come identificatore riutilizzabile.

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
    }

}

In UITableViewController ho questo codice,

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

Questo codice non rispetta errori, ma quando lo eseguo nel simulatore sembra così.

inserisci qui la descrizione dell'immagine

In UITableViewController nello storyboard non ho fatto nulla al cellulare. Identificatore vuoto e nessuna sottoclasse. Ho provato ad aggiungere la cella identificativo per il prototipo di cella e corse di nuovo ma ottengo lo stesso risultato.

Un altro errore che ho riscontrato è quando ho cercato di implementare il seguente metodo in 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]
}

Come mostrato nell'articolo che ho citato, ho cambiato la cellforma del tipo di parametro UITableViewCellin CustomOneCellcui è la mia sottoclasse di UITableViewCell. Ma ricevo il seguente errore,

Metodo di sostituzione con il selettore 'tableView: willDisplayCell: forRowAtIndexPath:' ha un tipo incompatibile '(UITableView !, CustomOneCell !, NSIndexPath!) -> ()'

Qualcuno ha idea di come risolvere questi errori? Questi sembravano funzionare bene in Objective-C.

Grazie.

EDIT: Ho appena notato che se cambio l'orientamento del simulatore in orizzontale e lo cambio in verticale, appaiono le celle! Non riuscivo ancora a capire cosa stesse succedendo. Ho caricato un progetto Xcode qui dimostrando il problema se hai tempo per una rapida occhiata.

Risposte:


213

Con Swift 5 e iOS 12.2, dovresti provare il seguente codice per risolvere il tuo 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
    }

}

L'immagine seguente mostra una serie di vincoli che funzionano con il codice fornito senza alcun messaggio di ambiguità di vincoli da Xcode:

inserisci qui la descrizione dell'immagine


1
Grazie per la risposta. Ma neanche quello ha funzionato. Devo modificare qualcosa nel controller della vista tabella? Perché è ancora impostato su prototipo di celle.
Isuru,

1
Continua a utilizzare le celle prototipo. Assicurati di aver impostato i buoni vincoli di layout automatico (se usi Layout automatico).
Imanou Petit,

1
Ho caricato un progetto di test qui dimostrando il problema che sto riscontrando. Puoi per favore dare un'occhiata se hai tempo?
Isuru,

1
Il tuo progetto di test lo conferma: sono stato in grado di far funzionare bene la tua app dopo aver impostato alcuni vincoli di layout automatico sulla tua cella personalizzata nel tuo file .xib. Guarda questo video se hai bisogno di saperne di più sul layout automatico.
Imanou Petit,

2
@KirillKudaev non è stato rinominato in Swift 4 ma in Swift 3: ho corretto la modifica.
Cor

30

Ecco il mio approccio con Swift 2 e Xcode 7.3. In questo esempio verrà utilizzato un singolo ViewController per caricare due file .xib, uno per UITableView e uno per UITableCellView.

inserisci qui la descrizione dell'immagine

Per questo esempio è possibile rilasciare un UITableView direttamente in un file .xib vuoto TableNib . All'interno, imposta il proprietario del file sulla tua classe ViewController e usa un outlet per fare riferimento a tableView.

inserisci qui la descrizione dell'immagine

e

inserisci qui la descrizione dell'immagine

Ora, nel tuo controller di visualizzazione, puoi delegare tableView come faresti normalmente, in questo modo

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

        ...

Per creare nuovamente la cella personalizzata, rilasciare un oggetto Cella vista tabella in un file .xib TableCellNib vuoto . Questa volta, nel file .xib della cella non è necessario specificare un "proprietario", ma è necessario specificare una classe personalizzata e un identificatore come "TableCellId"

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Crea la tua sottoclasse con tutti gli sbocchi di cui hai bisogno in questo modo

class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

Finalmente ... di nuovo nel tuo View Controller, puoi caricare e visualizzare tutto in questo modo

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)

}

Il codice mostra come è possibile semplicemente caricare e visualizzare un file pennino (la tabella), e in secondo luogo come registrare un pennino per l'uso delle celle.

Spero che questo ti aiuti!!!


2
puoi spiegare qual è il "tableCellId" in questa riga .... self.tableView.registerNib (cellNib, forCellReuseIdentifier: self.tableCellId) .... perché non hai definito cosa. e non puoi definire manualmente l'identificatore in xib .. nessuna opzione è lì per definirlo
PRADIP KUMAR

1
Nel builder di interfacce, quando si crea tableCell, nella "finestra di ispezione degli attributi" si definisce un identificatore. Lo stesso identificatore è quello che usi nel tuo controller per fare riferimento all'oggetto. let tableCellId = "myAwesomeCell". Ho aggiunto un'altra immagine per aiutarti.
internet-nico,

16

Swift 4

Pennino

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

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

9

Soluzione dettagliata con schermate

  1. Creare un file di interfaccia utente vuoto e denominarlo MyCustomCell.xib.

inserisci qui la descrizione dell'immagine

  1. Aggiungi a UITableViewCellcome root del tuo file xib e qualsiasi altro componente visivo che desideri.

inserisci qui la descrizione dell'immagine

  1. Crea un file di classe touch cacao con il nome della classe MyCustomCellcome sottoclasse di UITableViewCell.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

  1. Impostare la classe personalizzata e riutilizzare l'identificatore per la cella di visualizzazione tabella personalizzata.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

  1. Apri l'editor dell'assistente e ctrl+dragcrea sbocchi per i tuoi componenti visivi.

inserisci qui la descrizione dell'immagine

  1. Configura a UIViewControllerper usare la tua cella personalizzata.
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
        }
        ...
    }
}


Mi hai salvato la giornata. Avevo ... copiato MyHeaderView.swiftper cella personalizzata. La .swiftvista dell'intestazione for non è identifierpresente Table View Cellin Attribute Inspector. quindi ... si è verificato un errore di runtime.
mazend

A proposito ... perché abbiamo dichiarato lo stesso nome per l'identificatore in .swifte in tableView?.register(blahblah, forCellReuseIdentifier: "myCell")? Pensavo che uno di loro non fosse necessario ma ... ho scoperto che entrambi sono essenziali.
mazend

um .. Forse perché .. .xibper la cella personalizzata può contenere più UITableViewCellquindi .. .xibnon è abbastanza per .. trovare la cella corretta.
mazend

5

Non hai registrato il pennino come di seguito:

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

4

Un altro metodo che potrebbe funzionare per te (è come lo faccio) è la registrazione di una classe.

Supponiamo di creare una tabella personalizzata Visualizza come il seguente:

class UICustomTableViewCell: UITableViewCell {...}

Puoi quindi registrare questa cella in qualunque UITableViewController in cui la visualizzerai con "registerClass":

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

E puoi chiamarlo come ti aspetteresti nella cella per il metodo riga:

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

3

Per correggere l' errore "Override method ... has incompatible type ..." ho modificato la dichiarazione di funzione in

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

(era -> UITableViewCell!- con il punto esclamativo alla fine)


2

rapido 4.1.2

XI ter.

Crea ImageCell2.swift

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

}

passo 2 . Secondo la classe 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
    } 

}

2

Ho dovuto assicurarmi che durante la creazione dell'outlet per specificare che mi stavo collegando alla cella, non al proprietario dell'oggetto. Quando il menu sembra nominarlo, è necessario selezionarlo nel menu a discesa "oggetto". Ovviamente devi dichiarare anche la cella come classe, non solo "TableViewCellClass". Altrimenti continuerei a ottenere la classe non conforme alle chiavi.


1

Basta prendere un xib con la classe UITableViewCell . Impostare l'interfaccia utente come da reuirement e assegnare IBOutlet. Usalo in cellForRowAt () della vista tabella in questo modo:

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

inserisci qui la descrizione dell'immagine

100% funzionante senza problemi (testato)


dice che la cella di controllo? .labelName è nulla
Vignesh
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.