UITableViewCell personnalisé à partir de la pointe dans Swift

140

J'essaie de créer une cellule de vue de tableau personnalisée à partir d'une pointe. Je fais référence à cet article ici . Je suis confronté à deux problèmes.

J'ai créé un fichier .xib avec un objet UITableViewCell glissé dessus. J'ai créé une sous-classe de UITableViewCellet l' ai définie comme classe de la cellule et Cell comme identificateur réutilisable.

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
    }

}

Dans le UITableViewController, j'ai ce code,

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

Ce code est conforme sans erreur mais lorsque je l'exécute dans le simulateur, il ressemble à ceci.

entrez la description de l'image ici

Dans UITableViewController dans le storyboard, je n'ai rien fait à la cellule. Identifiant vide et pas de sous-classe. J'ai essayé d'ajouter l' identifiant de cellule à la cellule prototype et je l'ai relancé mais j'obtiens le même résultat.

Une autre erreur que j'ai rencontrée est, lorsque j'ai essayé d'implémenter la méthode suivante dans 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]
}

Comme indiqué dans l'article que j'ai mentionné, j'ai changé le cellformulaire de type du paramètre UITableViewCellà CustomOneCellqui est ma sous-classe de UITableViewCell. Mais j'obtiens l'erreur suivante,

Méthode de remplacement avec le sélecteur 'tableView: willDisplayCell: forRowAtIndexPath:' a un type incompatible '(UITableView !, CustomOneCell !, NSIndexPath!) -> ()'

Quelqu'un a une idée de la façon de résoudre ces erreurs? Celles-ci semblaient bien fonctionner en Objective-C.

Je vous remercie.

EDIT: Je viens de remarquer que si je change l'orientation du simulateur en paysage et le remet en portrait, les cellules apparaissent! Je n'arrivais toujours pas à comprendre ce qui se passait. J'ai téléchargé un projet Xcode ici démontrant le problème si vous avez le temps de jeter un coup d'œil rapide.

Isuru
la source

Réponses:

213

Avec Swift 5 et iOS 12.2, vous devriez essayer le code suivant pour résoudre votre problème:

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'image ci-dessous montre un ensemble de contraintes qui fonctionnent avec le code fourni sans aucun message d'ambiguïté de contraintes de Xcode:

entrez la description de l'image ici

Imanou Petit
la source
1
Merci pour la réponse. Mais cela n'a pas fonctionné non plus. Dois-je modifier quelque chose dans le contrôleur de vue de table? Parce qu'il est toujours configuré pour prototyper des cellules.
Isuru
1
Continuez à utiliser des cellules prototypes. Mais assurez-vous que vous avez défini les bonnes contraintes de mise en page automatique (si vous utilisez la mise en page automatique).
Imanou Petit
1
J'ai téléchargé un projet de test ici démontrant le problème que je rencontre. Pouvez-vous s'il vous plaît y jeter un œil si vous avez le temps?
Isuru
1
Votre projet de test le confirme: j'ai pu faire fonctionner votre application correctement après avoir défini des contraintes de mise en page automatique sur votre cellule personnalisée dans votre fichier .xib. Jetez un œil à cette vidéo si vous avez besoin d'en savoir plus sur la disposition automatique.
Imanou Petit
2
@KirillKudaev il n'a pas été renommé dans Swift 4 mais dans Swift 3: j'ai corrigé votre modification.
Cœur
30

Voici mon approche utilisant Swift 2 et Xcode 7.3. Cet exemple utilisera un seul ViewController pour charger deux fichiers .xib - un pour UITableView et un pour UITableCellView.

entrez la description de l'image ici

Pour cet exemple, vous pouvez déposer un UITableView directement dans un fichier TableNib .xib vide . À l'intérieur, définissez le propriétaire du fichier sur votre classe ViewController et utilisez une prise pour référencer la tableView.

entrez la description de l'image ici

et

entrez la description de l'image ici

Maintenant, dans votre contrôleur de vue, vous pouvez déléguer la tableView comme vous le feriez normalement, comme ceci

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

        ...

Pour créer votre cellule personnalisée, à nouveau, déposez un objet Cell View Cell dans un fichier TableCellNib .xib vide . Cette fois, dans le fichier .xib de la cellule, vous n'avez pas à spécifier de "propriétaire", mais vous devez spécifier une classe personnalisée et un identifiant comme "TableCellId"

entrez la description de l'image ici entrez la description de l'image ici

Créez votre sous-classe avec tous les points de vente dont vous avez besoin

class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

Enfin ... de retour dans votre View Controller, vous pouvez charger et afficher le tout comme ça

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)

}

Le code montre comment vous pouvez simplement charger et afficher un fichier nib (le tableau), et ensuite comment enregistrer une plume pour une utilisation cellulaire.

J'espère que cela t'aides!!!

internet-nico
la source
2
pouvez-vous expliquer quel est le "tableCellId" dans cette ligne .... self.tableView.registerNib (cellNib, forCellReuseIdentifier: self.tableCellId) .... parce que vous n'avez pas défini ce que cela. et vous ne pouvez pas définir manuellement l'identifiant dans xib .. aucune option n'est là pour le définir
PRADIP KUMAR
1
Dans le générateur d'interface, lorsque vous créez la tableCell, dans "l'inspecteur d'attributs" vous définissez un identifiant. Le même identifiant est celui que vous utilisez dans votre contrôleur pour référencer l'objet. let tableCellId = "myAwesomeCell". J'ai ajouté une autre image pour vous aider.
internet-nico
16

Swift 4

Enregistrer Nib

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

Dans 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
la source
9

Solution détaillée avec captures d'écran

  1. Créez un fichier d'interface utilisateur vide et nommez-le MyCustomCell.xib.

entrez la description de l'image ici

  1. Ajoutez a UITableViewCellcomme racine de votre fichier xib et de tout autre composant visuel souhaité.

entrez la description de l'image ici

  1. Créez un fichier de classe tactile cacao avec le nom de classe MyCustomCellcomme sous-classe de UITableViewCell.

entrez la description de l'image ici entrez la description de l'image ici

  1. Définissez la classe personnalisée et l'identificateur de réutilisation pour votre cellule de vue de tableau personnalisée.

entrez la description de l'image ici entrez la description de l'image ici

  1. Ouvrez l'éditeur d'assistant et ctrl+dragcréez des débouchés pour vos composants visuels.

entrez la description de l'image ici

  1. Configurez a UIViewControllerpour utiliser votre cellule personnalisée.
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
la source
Tu as sauvé ma journée. J'avais .. copié MyHeaderView.swiftpour une cellule personnalisée. La .swiftpour une vue en- tête n'a pas identifierdans Table View Cellla Attribute Inspector. donc ... une erreur d'exécution s'est produite.
mazend
Au fait .. pourquoi avons-nous déclaré le même nom pour l'identifiant in .swiftet in tableView?.register(blahblah, forCellReuseIdentifier: "myCell")? Je pensais que l'un d'eux n'était pas nécessaire mais .. J'ai trouvé que les deux sont essentiels.
mazend
euh .. C'est peut-être parce que .. .xibpour la cellule personnalisée peut contenir plusieurs UITableViewCelldonc .. .xibn'est pas suffisant pour .. trouver la cellule correcte.
mazend
5

Vous n'avez pas enregistré votre plume comme ci-dessous:

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

Une autre méthode qui peut fonctionner pour vous (c'est comme ça que je le fais) consiste à enregistrer une classe.

Supposons que vous créez un tableau personnalisé comme suit:

class UICustomTableViewCell: UITableViewCell {...}

Vous pouvez ensuite enregistrer cette cellule dans n'importe quel UITableViewController dans lequel vous l'afficherez avec "registerClass":

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

Et vous pouvez l'appeler comme vous vous en doutez dans la méthode cellule pour ligne:

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

Pour corriger l' erreur "Méthode de remplacement ... a un type incompatible ..." , j'ai changé la déclaration de fonction en

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

(était -> UITableViewCell!- avec un point d'exclamation à la fin)

tse
la source
2

swift 4.1.2

xib.

Créer ImageCell2.swift

Étape 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)
    }

}

étape 2 . Selon 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
    } 

}
user3263340
la source
2

Je devais m'assurer que lors de la création de la prise, je devais spécifier que je m'accrochais à la cellule, pas au propriétaire de l'objet. Lorsque le menu apparaît pour le nommer, vous devez le sélectionner dans le menu déroulant «objet». Bien sûr, vous devez également déclarer la cellule comme votre classe, pas seulement «TableViewCellClass». Sinon, je continuerais à obtenir la classe non conforme à la clé.

BuckleyJohnson
la source
1

Prenez simplement un xib avec la classe UITableViewCell . Définissez l'interface utilisateur selon les exigences et attribuez IBOutlet. Utilisez-le dans cellForRowAt () de la vue tableau comme ceci:

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

entrez la description de l'image ici

100% fonctionnant sans aucun problème (testé)

Monsieur Javed Multani
la source
il dit que la cellule de contrôle? .labelName est nul
Vignesh