Niestandardowe UITableViewCell z końcówki w języku Swift


139

Próbuję utworzyć niestandardową komórkę widoku tabeli ze stalówki. Mówię o tym artykule tutaj . Mam dwa problemy.

Utworzyłem plik .xib z przeciągniętym na niego obiektem UITableViewCell. Utworzyłem podklasę UITableViewCelli ustawiłem ją jako klasę komórki i Cell jako identyfikator wielokrotnego użytku.

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
    }

}

W UITableViewController mam ten kod,

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

Ten kod jest zgodny bez błędów, ale kiedy uruchamiam go w symulatorze, wygląda to tak.

wprowadź opis obrazu tutaj

W UITableViewController w scenorysie nie zrobiłem nic z komórką. Pusty identyfikator i brak podklasy. Próbowałem dodać identyfikator komórki do komórki prototypowej i uruchomiłem ją ponownie, ale otrzymałem ten sam wynik.

Innym błędem, z którym się spotkałem, jest próba zaimplementowania następującej metody w 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]
}

Jak pokazano w artykule wspomniałem Zmieniłem cellformę typu parametru UITableViewCelldo CustomOneCellktórego jest moim podklasy UITableViewCell. Ale pojawia się następujący błąd,

Przesłanianie metody za pomocą selektora „tableView: willDisplayCell: forRowAtIndexPath:„ ma niezgodny typ ”(UITableView !, CustomOneCell !, NSIndexPath!) -> ()”

Czy ktoś ma pomysł, jak rozwiązać te błędy? Wydawało się, że działają dobrze w Objective-C.

Dziękuję Ci.

EDYCJA: Właśnie zauważyłem, że jeśli zmienię orientację symulatora na poziomą i wrócę do portretu, komórki się pojawią! Nadal nie mogłem dowiedzieć się, co się dzieje. Wrzuciłem tutaj projekt Xcode pokazujący problem, jeśli masz czas na szybkie spojrzenie.

Odpowiedzi:


213

W przypadku Swift 5 i iOS 12.2 powinieneś wypróbować następujący kod, aby rozwiązać problem:

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
    }

}

Poniższy obraz przedstawia zestaw ograniczeń, które działają z podanym kodem bez komunikatu o niejednoznaczności ograniczeń z Xcode:

wprowadź opis obrazu tutaj


1
Dzięki za odpowiedzi. Ale to też nie zadziałało. Czy muszę cokolwiek zmieniać w kontrolerze widoku tabeli? Ponieważ nadal jest ustawiony na komórki prototypowe.
— Isuru

1
Nadal używaj prototypowych komórek. Ale upewnij się, że ustawiłeś dobre ograniczenia automatycznego układu (jeśli używasz automatycznego układu).
— Imanou Petit

1
Wrzuciłem tutaj projekt testowy demonstrujący problem, który mam. Czy możesz na to spojrzeć, jeśli masz czas?
— Isuru

1
Twój projekt testowy to potwierdza: udało mi się sprawić, by Twoja aplikacja działała dobrze po ustawieniu niektórych ograniczeń układu automatycznego na niestandardową komórkę w pliku .xib. Obejrzyj ten film, jeśli chcesz dowiedzieć się więcej o automatycznym układzie.
— Imanou Petit

2
@KirillKudaev nazwa nie została zmieniona w Swift 4, ale w Swift 3: Poprawiłem twoją edycję.
— Cœur,

30

Oto moje podejście przy użyciu Swift 2 i Xcode 7.3. W tym przykładzie użyje jednego ViewController do załadowania dwóch plików xib - jednego dla UITableView i jednego dla UITableCellView.

wprowadź opis obrazu tutaj

W tym przykładzie możesz upuścić UITableView bezpośrednio do pustego pliku TableNib .xib. Wewnątrz ustaw właściciela pliku na klasę ViewController i użyj gniazdka, aby odwołać się do tableView.

wprowadź opis obrazu tutaj

i

wprowadź opis obrazu tutaj

Teraz w kontrolerze widoku możesz delegować tableView w normalny sposób

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

        ...

Aby utworzyć komórkę niestandardową, ponownie upuść obiekt komórki widoku tabeli do pustego pliku TableCellNib .xib. Tym razem w pliku .xib komórki nie musisz określać „właściciela”, ale musisz określić klasę niestandardową i identyfikator, taki jak „TableCellId”

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Utwórz podklasę z dowolnymi gniazdami, których potrzebujesz

class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

Wreszcie ... z powrotem w kontrolerze widoku możesz załadować i wyświetlić całość w ten sposób

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)

}

Kod pokazuje, jak można po prostu załadować i wyświetlić plik nib (tabelę), a po drugie, jak zarejestrować końcówkę do użytku w komórce.

Mam nadzieję że to pomoże!!!


2
czy możesz wyjaśnić, co to jest "tableCellId" w tym wierszu .... self.tableView.registerNib (cellNib, forCellReuseIdentifier: self.tableCellId) .... ponieważ nie zdefiniowałeś, co to jest. i nie możesz ręcznie zdefiniować identyfikatora w xib .. nie ma opcji, aby go zdefiniować
— PRADIP KUMAR

1
W kreatorze interfejsu, kiedy tworzysz tableCell, w "Inspektorze atrybutów" definiujesz identyfikator. Ten sam identyfikator jest używany w kontrolerze do odwoływania się do obiektu. let tableCellId = "myAwesomeCell". Dodałem kolejny obraz, aby ci pomóc.
— internet-nico

16

Szybki 4

Zarejestruj Nib

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

W 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

Szczegółowe rozwiązanie ze zrzutami ekranu

  1. Utwórz pusty plik interfejsu użytkownika i nazwij go MyCustomCell.xib.

wprowadź opis obrazu tutaj

  1. Dodaj UITableViewCelljako katalog główny pliku xib i wszelkie inne komponenty wizualne, które chcesz.

wprowadź opis obrazu tutaj

  1. Utwórz plik klasy dotykowej cocoa z nazwą klasy MyCustomCelljako podklasą UITableViewCell.

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

  1. Ustaw klasę niestandardową i identyfikator ponownego wykorzystania dla komórki niestandardowego widoku tabeli.

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

  1. Otwórz asystent edytora i ctrl+dragutwórz wyjścia dla swoich komponentów wizualnych.

wprowadź opis obrazu tutaj

  1. Skonfiguruj, UIViewControlleraby używał własnej komórki.
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
        }
        ...
    }
}

Uratowałeś mi dzień. Skopiowałem ... MyHeaderView.swiftdla niestandardowej komórki. .swiftNa nagłówku widoku nie ma identifierw Table View Cellw Attribute Inspector. więc ... wystąpił błąd czasu wykonywania.
— mazend

Swoją drogą… dlaczego zadeklarowaliśmy tę samą nazwę dla identyfikatora w .swifti w tableView?.register(blahblah, forCellReuseIdentifier: "myCell")? Myślałem, że jeden z nich nie jest konieczny, ale ... stwierdziłem, że oba są niezbędne.
— mazend

um .. Może dlatego, że… .xibdla komórki niestandardowej może zawierać wiele, UITableViewCellwięc… .xibnie wystarczy… znaleźć właściwą komórkę.
— mazend

5

Nie zarejestrowałeś swojej stalówki jak poniżej:

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

4

Inną metodą, która może zadziałać dla Ciebie (tak to robię) jest rejestracja klasy.

Załóżmy, że tworzysz niestandardowy tableView w następujący sposób:

class UICustomTableViewCell: UITableViewCell {...}

Następnie możesz zarejestrować tę komórkę w dowolnym kontrolerze UITableViewController, w którym będziesz ją wyświetlać za pomocą „registerClass”:

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

I możesz to nazwać tak, jak można się spodziewać w komórce dla metody wiersza:

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

3

Aby naprawić błąd „Przesłanianie metody ... ma niezgodny typ ...” zmieniłem deklarację funkcji na

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

(było -> UITableViewCell!- z wykrzyknikiem na końcu)


2

szybki 4.1.2

xib.

Utwórz ImageCell2.swift

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

}

krok 2 . Zgodnie z klasą 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

Musiałem się upewnić, że podczas tworzenia gniazdka określam, że podłączam się do komórki, a nie do właściciela obiektu. Kiedy pojawi się menu, aby je nazwać, musisz je wybrać z menu rozwijanego „obiekt”. Oczywiście musisz również zadeklarować komórkę jako swoją klasę, a nie tylko „TableViewCellClass”. W przeciwnym razie klasa nie jest zgodna z kluczem.


1

Po prostu weź xib z klasą UITableViewCell . Ustaw interfejs użytkownika zgodnie z wymaganiami i przypisz IBOutlet. Użyj go w cellForRowAt () widoku tabeli w następujący sposób:

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

wprowadź opis obrazu tutaj

100% działa bez problemu (testowane)


mówi, że komórka kontrolna? .labelName jest zerowa
— Vignesh
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.