Пользовательский UITableViewCell из пера в Swift

143

Я пытаюсь создать настраиваемую ячейку представления таблицы из пера. Я имею в виду эту статью здесь . У меня две проблемы.

Я создал файл .xib с перетащенным на него объектом UITableViewCell. Я создал подкласс UITableViewCellи установил его как класс ячейки, а Cell как повторно используемый идентификатор.

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
    }

}

В UITableViewController у меня есть этот код,

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

Этот код не содержит ошибок, но когда я запускаю его в симуляторе, он выглядит так.

введите описание изображения здесь

В UITableViewController в раскадровке я ничего не сделал с ячейкой. Пустой идентификатор и без подкласса. Я попытался добавить идентификатор Cell в ячейку прототипа и снова запустил его, но получил тот же результат.

Еще одна ошибка, с которой я столкнулся, - это когда я пытался реализовать следующий метод в 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]
}

Как показано в этой статье я упоминал , что я изменил cellформу типа параметра UITableViewCellв CustomOneCellкоторое мой подкласс UITableViewCell. Но я получаю следующую ошибку:

Метод переопределения с помощью селектора tableView: willDisplayCell: forRowAtIndexPath: имеет несовместимый тип (UITableView !, CustomOneCell !, NSIndexPath!) -> () '

Кто-нибудь знает, как исправить эти ошибки? Похоже, они отлично работали в Objective-C.

Спасибо.

РЕДАКТИРОВАТЬ: Я только что заметил, что если я изменю ориентацию симулятора на альбомную и верну ее обратно в портретную, появятся ячейки! Я все еще не мог понять, что происходит. Я загрузил здесь проект Xcode, демонстрирующий проблему, если у вас есть время быстро взглянуть.

Исуру
источник

Ответы:

217

В Swift 5 и iOS 12.2 вы должны попробовать следующий код, чтобы решить вашу проблему:

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
    }

}

На изображении ниже показан набор ограничений, которые работают с предоставленным кодом без какого-либо сообщения о неоднозначности ограничений от Xcode:

введите описание изображения здесь

Иману Пети
источник
2
Спасибо за ответ. Но и это не сработало. Нужно ли мне что-то менять в контроллере табличного представления? Потому что все еще настроены на прототипы ячеек.
Isuru
1
Продолжайте использовать прототипы ячеек. Но убедитесь, что вы установили хорошие ограничения автоматического макета (если вы используете автоматический макет).
Иману Пети
1
Я загрузил сюда тестовый проект, демонстрирующий мою проблему. Не могли бы вы взглянуть на него, если у вас есть время?
Isuru
1
Ваш тестовый проект подтверждает это: мне удалось заставить ваше приложение работать нормально после того, как я установил некоторые ограничения автоматического макета для вашей пользовательской ячейки в вашем файле .xib. Посмотрите это видео, если вам нужно больше узнать об автоматической компоновке.
Иману Пети
2
@KirillKudaev он не был переименован в Swift 4, но в Swift 3: я исправил вашу правку.
Cœur
30

Вот мой подход с использованием Swift 2 и Xcode 7.3. В этом примере будет использоваться один ViewController для загрузки двух файлов .xib - одного для UITableView и одного для UITableCellView.

введите описание изображения здесь

В этом примере вы можете поместить UITableView прямо в пустой файл TableNib .xib. Внутри установите владельцем файла свой класс ViewController и используйте выход для ссылки на tableView.

введите описание изображения здесь

а также

введите описание изображения здесь

Теперь в вашем контроллере представления вы можете делегировать tableView, как обычно, например

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

        ...

Чтобы создать пользовательскую ячейку, снова перетащите объект Table View Cell в пустой файл TableCellNib .xib. На этот раз в файле .xib ячейки вам не нужно указывать «владельца», но вам нужно указать настраиваемый класс и идентификатор, например «TableCellId».

введите описание изображения здесь введите описание изображения здесь

Создайте свой подкласс с любыми выходами, которые вам нужны.

class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

Наконец ... вернувшись в свой View Controller, вы можете загрузить и отобразить все это так:

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)

}

В коде показано, как можно просто загрузить и отобразить файл пера (таблицу), а во-вторых, как зарегистрировать перо для использования в ячейке.

Надеюсь это поможет!!!

Интернет-Нико
источник
2
Вы можете объяснить, что такое «tableCellId» в этой строке .... self.tableView.registerNib (cellNib, forCellReuseIdentifier: self.tableCellId) .... потому что вы не определили, что это такое. и вы не можете вручную определить идентификатор в xib .. нет возможности определить его
PRADIP KUMAR
1
В конструкторе интерфейса, когда вы создаете tableCell, в «инспекторе атрибутов» вы определяете идентификатор. Тот же идентификатор используется в вашем контроллере для ссылки на объект. let tableCellId = "myAwesomeCell". Я добавил еще одно изображение, чтобы помочь вам.
internet-nico
16

Swift 4

Зарегистрировать перо

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

В источнике данных TableView

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
          guard let cell = tableView.dequeueReusableCell(withIdentifier: "MissionCell", for: indexPath) as? MissionCell else { return UITableViewCell() }
          return cell
    }
Гурджиндер Сингх
источник
11

Подробное решение со скриншотами

  1. Создайте пустой файл пользовательского интерфейса и назовите его MyCustomCell.xib.

введите описание изображения здесь

  1. Добавьте в UITableViewCellкачестве корня вашего файла xib и любых других визуальных компонентов, которые вы хотите.

введите описание изображения здесь

  1. Создайте файл класса касания какао с именем класса MyCustomCellв качестве подкласса UITableViewCell.

введите описание изображения здесь введите описание изображения здесь

  1. Задайте настраиваемый класс и идентификатор повторного использования для ячейки настраиваемого табличного представления.

введите описание изображения здесь введите описание изображения здесь

  1. Откройте помощник редактора и ctrl+dragсоздайте выходы для ваших визуальных компонентов.

введите описание изображения здесь

  1. Настройте UIViewControllerдля использования пользовательской ячейки.
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
        }
        ...
    }
}

Дерек Сойке
источник
Ты спас мне день. Я ... скопировал MyHeaderView.swiftдля нестандартной ячейки. .swiftДля заголовка вид не имеет identifierв Table View Cellв Attribute Inspector. итак ... произошла ошибка времени выполнения.
mazend
Кстати .. почему мы объявляем одно и то же имя для идентификатора in .swiftи in tableView?.register(blahblah, forCellReuseIdentifier: "myCell")? Я думал, что в одном из них нет необходимости, но .. Я обнаружил, что оба необходимы.
mazend
гм .. Это может быть потому, что .. .xibдля пользовательской ячейки может быть несколько, UITableViewCellпоэтому .. .xibнедостаточно, чтобы .. найти правильную ячейку.
mazend
5

Вы не зарегистрировали перо, как показано ниже:

tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
Суджан
источник
4

Другой способ, который может сработать для вас (как я это делаю), - это регистрация класса.

Предположим, вы создаете настраиваемый tableView, как показано ниже:

class UICustomTableViewCell: UITableViewCell {...}

Затем вы можете зарегистрировать эту ячейку в любом UITableViewController, в котором вы будете отображать ее, с помощью "registerClass":

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

И вы можете вызвать его, как и следовало ожидать в ячейке для метода строки:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("UICustomTableViewCellIdentifier", forIndexPath: indexPath) as! UICustomTableViewCell
    return cell
}
Итан Кей
источник
3

Чтобы исправить ошибку «Метод переопределения ... имеет несовместимый тип ...», я изменил объявление функции на

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

(было -> UITableViewCell!- с восклицательным знаком в конце)

це
источник
3

быстрый 4.1.2

xib.

Создать ImageCell2.swift

Шаг 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)
    }

}

шаг 2 . Согласно классу 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
источник
2

Я должен был убедиться, что при создании розетки указывать, что я подключаюсь к ячейке, а не к владельцу объекта. Когда появится меню, чтобы назвать его, вы должны выбрать его в раскрывающемся меню «объект». Конечно, вы также должны объявить ячейку своим классом, а не просто TableViewCellClass. В противном случае я бы продолжал получать класс, не соответствующий ключу.

БаклиДжонсон
источник
1

Просто возьмите xib с классом UITableViewCell . Установите пользовательский интерфейс в соответствии с требованиями и назначьте IBOutlet. Используйте его в cellForRowAt () табличного представления следующим образом:

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

введите описание изображения здесь

100% работает без каких-либо проблем (проверено)

Г-н Джавед Мултани
источник
в нем говорится, что контрольная ячейка? .labelName равна нулю
Виньеш
0

Эту строку добавьте в TableViewячейку:

static var nib  : UINib{

           return UINib(nibName: identifier, bundle: nil)

       }

       static var identifier : String{

           return String(describing: self)

       }

    

And register in viewcontroller like



//This line use in viewdid load

tableview.register(TopDealLikedTableViewCell.nib, forCellReuseIdentifier: TopDealLikedTableViewCell.identifier)

// cell for row at indexpath

if let cell = tableView.dequeueReusableCell(withIdentifier:

    TopDealLikedTableViewCell.identifier) as? TopDealLikedTableViewCell{

           return cell

  }

return UITableViewCell()
jenish.x
источник