Benutzerdefinierte UITableViewCell von der Feder in Swift

139

Ich versuche, eine benutzerdefinierte Tabellenansichtszelle aus einer Schreibfeder zu erstellen. Ich beziehe mich auf diesen Artikel hier . Ich stehe vor zwei Problemen.

Ich habe eine .xib-Datei mit einem darauf gezogenen UITableViewCell-Objekt erstellt. Ich habe eine Unterklasse von erstellt UITableViewCellund sie als Klasse der Zelle und Zelle als wiederverwendbaren Bezeichner festgelegt.

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
    }

}

Im UITableViewController habe ich diesen 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
    }
}

Dieser Code ist fehlerfrei, aber wenn ich ihn im Simulator ausführe, sieht er so aus.

Geben Sie hier die Bildbeschreibung ein

Im UITableViewController im Storyboard habe ich nichts mit der Zelle gemacht. Leere Kennung und keine Unterklasse. Ich habe versucht, die Zellkennung zur Prototypzelle hinzuzufügen, und sie erneut ausgeführt, aber ich erhalte das gleiche Ergebnis.

Ein weiterer Fehler, mit dem ich konfrontiert war, war der Versuch, die folgende Methode im UITableViewController zu implementieren.

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

Wie in dem Artikel gezeigt, den ich erwähnt habe, habe ich die Typform des cellParameters geändert, UITableViewCellin CustomOneCelldie meine Unterklasse von UITableViewCell ist. Aber ich bekomme folgenden Fehler:

Überschreibende Methode mit Selektor 'tableView: willDisplayCell: forRowAtIndexPath:' hat inkompatiblen Typ '(UITableView!, CustomOneCell!, NSIndexPath!) -> ()'

Hat jemand eine Idee, wie man diese Fehler behebt? Diese schienen in Objective-C gut zu funktionieren.

Danke dir.

BEARBEITEN: Mir ist gerade aufgefallen, dass die Zellen angezeigt werden, wenn ich die Ausrichtung des Simulators auf Querformat ändere und wieder auf Hochformat umstelle! Ich konnte immer noch nicht herausfinden, was los ist. Ich lud ein Xcode - Projekt hier demonstriert das Problem , wenn Sie Zeit für einen kurzen Blick.

Isuru
quelle

Antworten:

213

Mit Swift 5 und iOS 12.2 sollten Sie den folgenden Code ausprobieren, um Ihr Problem zu lösen:

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
    }

}

Das folgende Bild zeigt eine Reihe von Einschränkungen, die mit dem bereitgestellten Code ohne eine Mehrdeutigkeitsnachricht für Einschränkungen von Xcode funktionieren:

Geben Sie hier die Bildbeschreibung ein

Imanou Petit
quelle
1
Danke für die Antwort. Aber das hat auch nicht funktioniert. Muss ich etwas im Table View Controller ändern? Weil es immer noch Prototypzellen ist.
Isuru
1
Verwenden Sie weiterhin Prototypzellen. Stellen Sie jedoch sicher, dass Sie die guten Einschränkungen für das automatische Layout festgelegt haben (wenn Sie das automatische Layout verwenden).
Imanou Petit
1
Ich lud ein Testprojekt hier das Problem demonstriert ich habe. Können Sie es sich bitte ansehen, wenn Sie Zeit haben?
Isuru
1
Ihr Testprojekt bestätigt dies: Ich konnte Ihre App einwandfrei funktionieren lassen, nachdem ich einige Einschränkungen für das automatische Layout Ihrer benutzerdefinierten Zelle in Ihrer .xib-Datei festgelegt hatte. Schauen Sie sich dieses Video an, wenn Sie mehr über das automatische Layout erfahren möchten.
Imanou Petit
2
@KirillKudaev wurde nicht in Swift 4 umbenannt, sondern in Swift 3: Ich habe Ihre Bearbeitung korrigiert.
Cœur
30

Hier ist mein Ansatz mit Swift 2 und Xcode 7.3. In diesem Beispiel wird ein einzelner ViewController zum Laden von zwei .xib-Dateien verwendet - eine für eine UITableView und eine für die UITableCellView.

Geben Sie hier die Bildbeschreibung ein

In diesem Beispiel können Sie eine UITableView direkt in eine leere TableNib .xib-Datei ablegen. Legen Sie im Inneren den Dateieigentümer auf Ihre ViewController-Klasse fest und verwenden Sie einen Ausgang, um auf die tableView zu verweisen.

Geben Sie hier die Bildbeschreibung ein

und

Geben Sie hier die Bildbeschreibung ein

Jetzt können Sie in Ihrem View Controller die tableView wie gewohnt delegieren

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

        ...

Um Ihre benutzerdefinierte Zelle erneut zu erstellen, legen Sie ein Tabellenansichtszellenobjekt in einer leeren TableCellNib .xib-Datei ab. Dieses Mal müssen Sie in der .xib-Zellendatei keinen "Eigentümer" angeben, aber Sie müssen eine benutzerdefinierte Klasse und einen Bezeichner wie "TableCellId" angeben.

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Erstellen Sie Ihre Unterklasse mit den gewünschten Verkaufsstellen

class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

Endlich ... zurück in Ihrem View Controller können Sie das Ganze so laden und anzeigen

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)

}

Der Code zeigt, wie Sie einfach eine Nib-Datei (die Tabelle) laden und anzeigen können, und zweitens, wie Sie eine Nib für die Verwendung in Zellen registrieren.

Hoffe das hilft!!!

Internet-Nico
quelle
2
Können Sie erklären, was die "tableCellId" in dieser Zeile ist? self.tableView.registerNib (cellNib, forCellReuseIdentifier: self.tableCellId) ... weil Sie nicht definiert haben, was das ist. und Sie können den Bezeichner in xib nicht manuell definieren. Es gibt keine Option, um ihn zu definieren
PRADIP KUMAR
1
Wenn Sie im Interface Builder beim Erstellen der Tabellenzelle im "Attributinspektor" einen Bezeichner definieren. Dieselbe Kennung verwenden Sie in Ihrem Controller, um auf das Objekt zu verweisen. let tableCellId = "myAwesomeCell". Ich habe ein weiteres Bild hinzugefügt, um Ihnen zu helfen.
Internet-Nico
16

Swift 4

Registrieren Sie Nib

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
    }
Gurjinder Singh
quelle
9

Detaillierte Lösung mit Screenshots

  1. Erstellen Sie eine leere Benutzeroberflächendatei und benennen Sie sie MyCustomCell.xib.

Geben Sie hier die Bildbeschreibung ein

  1. Fügen Sie a UITableViewCellals Stammverzeichnis Ihrer xib-Datei und aller anderen gewünschten visuellen Komponenten hinzu.

Geben Sie hier die Bildbeschreibung ein

  1. Erstellen Sie eine Cocoa Touch-Klassendatei mit dem Klassennamen MyCustomCellals Unterklasse von UITableViewCell.

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

  1. Legen Sie die benutzerdefinierte Klasse und die Wiederverwendungskennung für Ihre benutzerdefinierte Tabellenansichtszelle fest.

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

  1. Öffnen Sie den Assistenten-Editor und ctrl+dragerstellen Sie Ausgänge für Ihre visuellen Komponenten.

Geben Sie hier die Bildbeschreibung ein

  1. Konfigurieren Sie a UIViewController, um Ihre benutzerdefinierte Zelle zu verwenden.
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
quelle
Du hast meinen Tag gerettet. Ich hatte .. MyHeaderView.swiftfür benutzerdefinierte Zelle kopiert . Die .swiftAnsicht für Kopf muss nicht identifierin Table View Cellin der Attribute Inspector. also ... Laufzeitfehler aufgetreten.
Mazend
Übrigens ... warum haben wir den gleichen Namen für die Kennung in .swiftund in deklariert tableView?.register(blahblah, forCellReuseIdentifier: "myCell")? Ich dachte, einer von ihnen ist nicht notwendig, aber ... ich fand, dass beide wesentlich sind.
Mazend
ähm .. Es liegt vielleicht daran, dass .. .xibfür benutzerdefinierte Zellen mehrere enthalten kann, UITableViewCellalso .. .xibnicht ausreicht, um .. die richtige Zelle zu finden.
Mazend
5

Sie haben Ihre Feder nicht wie folgt registriert:

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

Eine andere Methode, die für Sie funktionieren kann (so mache ich das), ist das Registrieren einer Klasse.

Angenommen, Sie erstellen eine benutzerdefinierte Tabellenansicht wie folgt:

class UICustomTableViewCell: UITableViewCell {...}

Sie können diese Zelle dann in dem UITableViewController registrieren, in dem Sie sie mit "registerClass" anzeigen möchten:

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

Und Sie können es so aufrufen, wie Sie es in der Zelle für die Zeilenmethode erwarten würden:

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

Zur Behebung des Fehlers "Überschreibende Methode ... hat inkompatiblen Typ ..." habe ich die Funktionsdeklaration in geändert

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

(war -> UITableViewCell!- mit Ausrufezeichen am Ende)

tse
quelle
2

schnell 4.1.2

xib.

Erstellen Sie ImageCell2.swift

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

}

Schritt 2 . Entsprechend der Viewcontroller-Klasse

  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
quelle
2

Ich musste beim Erstellen der Steckdose sicherstellen, dass ich mich an die Zelle und nicht an den Eigentümer des Objekts anschloss. Wenn das Menü angezeigt wird, müssen Sie es im Dropdown-Menü "Objekt" auswählen. Natürlich müssen Sie die Zelle auch als Ihre Klasse deklarieren, nicht nur 'TableViewCellClass'. Andernfalls würde ich die Klasse weiterhin nicht schlüsselkonform machen.

Buckley Johnson
quelle
1

Nehmen Sie einfach eine xib mit der Klasse UITableViewCell . Stellen Sie die Benutzeroberfläche gemäß den Anforderungen ein und weisen Sie das IBOutlet zu. Verwenden Sie es in cellForRowAt () der Tabellenansicht wie folgt:

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

Geben Sie hier die Bildbeschreibung ein

100% ohne Probleme arbeiten (getestet)

Mr.Javed Multani
quelle
Es heißt, die Kontrollzelle? .labelName ist null
Vignesh