Подтвердить что ты не робот

Использование программно созданного подкласса UITableViewCell в Swift

Я работаю над представлением таблицы с пользовательской ячейкой, которая была создана программно, без использования IB. Я осмотрел Google и спросил в NSChat, но я все еще не могу заставить его работать. Он просто отображает табличный вид по умолчанию. Спасибо заранее!

EventCell.swift

import UIKit

class EventCell: UITableViewCell {

    var eventName: UILabel = UILabel()
    var eventCity: UILabel = UILabel()
    var eventTime: UILabel = UILabel()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        self.contentView.addSubview(eventName)
        self.contentView.addSubview(eventCity)
        self.contentView.addSubview(eventTime)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        eventName = UILabel(frame: CGRectMake(20, 10, self.bounds.size.width - 40, 25))
        eventCity = UILabel(frame: CGRectMake(0, 0, 0, 0))
        eventTime = UILabel(frame: CGRectMake(0, 0, 0, 0))

    }

}

ViewController.swift

class ViewController: UITableViewController, UITableViewDelegate {

    var events: Dictionary<String, [String]> = ["0": ["Monroe Family", "La Cañada", "8:30"]]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self;
        tableView.delegate = self;

        tableView.registerClass(EventCell.self, forCellReuseIdentifier: "EventCell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension;
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return events.count
    }

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

        var cellIdendifier: String = "EventCell"

        var cell: EventCell = tableView.dequeueReusableCellWithIdentifier(cellIdendifier, forIndexPath: indexPath) as EventCell
        cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier)

        if let i = events[String(indexPath.row)] {
            cell.eventName.text = i[0]
            cell.eventCity.text = i[1]
            cell.eventTime.text = i[2]
        }

        cell.sizeToFit()

        return cell


    }
}
4b9b3361

Ответ 1

Хорошо сначала несколько важных комментариев.

Сначала, вы бесполезно создаете свою собственную новую ячейку каждый раз, когда представление таблицы запрашивает ячейку вместо повторного использования старых ячеек. Вы должны удалить эту строку:

cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier)

Это необязательно, потому что dequeue автоматически создает новые ячейки по мере необходимости на основе класса, который вы зарегистрировали для данного идентификатора.

Второй, вы не должны использовать границы главного экрана при выделении кода. Это сломается, если ваш вид таблицы не является полной шириной. Вместо этого вы можете использовать self.bounds, поэтому он всегда относится к самой ячейке.

Третий, вы не должны вызывать setNeedsLayout или layoutIfNeeded, потому что, если этот метод вызывается, он уже все время откладывает.

Четвертый, вы должны зарегистрировать свой класс ячеек таблицы перед установкой источника данных табличного представления на всякий случай, когда UITableView каждый начинает запрашивать данные из источника данных, когда установлен источник данных.

Пятый, два из ваших подзонов имеют размер 0,0, поэтому они все равно не появятся.

Если этот код действительно работает без сбоев, вы создаете EventCells, потому что вы выполняете принудительное кастинг из результата dequeueReusableCellWithIdentifier:forIndexPath. Это означает, что у вас просто есть проблема с компоновкой/отображением/данными.

Ответ 2

У меня был тот же вопрос. Я применил совет в ответе @drewag, но были некоторые дополнительные проблемы. Ниже приведен пример, демонстрирующий концептуальную концепцию, показывающую, как использовать программно созданный подкласс UITableViewCell.

Ниже показано, как это должно выглядеть. Желтые прямоугольники представляют собой UILabel subviews в пользовательских ячейках.

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

Код

Вот подкласс UITableViewCell. Это задание - инициализировать ячейку и ее содержимое, добавить подзаголовки и компоновку подвью.

import UIKit
class MyCustomCell: UITableViewCell {

    var myLabel = UILabel()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        myLabel.backgroundColor = UIColor.yellowColor()
        self.contentView.addSubview(myLabel)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        myLabel.frame = CGRect(x: 20, y: 0, width: 70, height: 30)
    }
}

Вот код контроллера вида.

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    let myArray = ["one", "two", "three", "four"]
    let cellReuseIdendifier = "cell"


    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerClass(MyCustomCell.self, forCellReuseIdentifier: cellReuseIdendifier)

        tableView.dataSource = self
        tableView.delegate = self
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myArray.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdendifier, forIndexPath: indexPath) as! MyCustomCell
        cell.myLabel.text = myArray[indexPath.row]

        return cell
    }
}

Примечания:

  • Я использовал обычный UIViewController, а не UITableViewController. Если вы используете UITableViewController, то удалите ссылки протокола UITableViewDelegate и UITableViewDataSource, поскольку они являются избыточными для UITableViewController. Вам также не понадобится строка @IBOutlet tableView.
  • Основная проблема, которую я нашел в исходном вопросе, была eventName = UILabel(frame: CGRectMake(...)). Вместо этого он должен был eventName.frame = CGRect(...)

Ответ 3

Ваш конкретный код не работает, потому что он создает новые UILabels в EventCell.layoutSubviews. В дерево просмотра добавляются только начальные UILabels, и их размеры, вероятно, равны 0'd.

Я предполагаю, что вы хотели обновить свои фреймы, например. обновите метод класса EventCell:

override func layoutSubviews() {
    super.layoutSubviews()

    eventName.frame = CGRectMake(20, 10, self.bounds.size.width - 40, 25)
    eventCity.frame = CGRectMake(<actual size>)
    eventTime.frame = CGRectMake(<actual size>)
}

Ответ 4

Вы можете использовать ленивый экземпляр в быстром темпе, чтобы не упасть в layoutSubviews/просмотреть жизненный цикл.

class EventCell: UITableViewCell {
  lazy public  var lblName = {
    return UILabel (frame: CGRectMake(10, 0, self.bounds.size.width , 40))
    }()

 lazy public  var lblCity = {
    return UILabel (frame: CGRectMake(10, 40, self.bounds.size.width , 20))
    }()
 lazy public  var lblTime = {
    return UILabel (frame: CGRectMake(self.bounds.size.width - 80, 10, , 25))
    }()


  override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    self.contentView.addSubview(lblName)
    self.contentView.addSubview(lblCity)
    self.contentView.addSubview(lblTime)
  }

}