Использование программно созданного подкласса 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
}
}
4 ответа
Хорошо, сначала несколько важных комментариев.
Во-первых, вы без необходимости создаете свою собственную новую ячейку каждый раз, когда табличное представление запрашивает ячейку, вместо того, чтобы повторно использовать старые ячейки. Вы должны удалить эту строку:
cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier)
В этом нет необходимости, поскольку dequeue автоматически создает новые ячейки по мере необходимости на основе класса, который вы зарегистрировали для данного идентификатора.
Во-вторых, вы не должны использовать границы основного экрана при размещении вашего кода. Это сломается, если ваш вид таблицы не на всю ширину. Вместо этого вы можете использовать self.bounds
так что это всегда относительно самой клетки.
В-третьих, вы не должны вызывать setNeedsLayout или layoutIfNeeded, потому что, если этот метод вызывается, он уже все раскладывает снова.
В-четвертых, вы должны зарегистрировать свой класс ячеек табличного представления перед настройкой источника данных табличного представления на тот случай, если UITableView каждый начинает запрашивать данные из источника данных, когда источник данных установлен.
В-пятых, два ваших подпредставления имеют размер 0,0, поэтому они все равно не появятся.
Если этот код действительно работает без сбоев, то вы создаете EventCells, потому что вы делаете принудительное приведение из результата dequeueReusableCellWithIdentifier:forIndexPath
, Это означает, что у вас просто проблема с макетом / отображением / данными.
У меня такой же вопрос. Я применил совет в ответе @drewag, но были некоторые дополнительные проблемы. Ниже приведен простой пример, подтверждающий концепцию, который показывает, как использовать программно созданный UITableViewCell
подкласс.
Изображение ниже, как это должно выглядеть. Желтые прямоугольники UILabel
подпредставления в пользовательских ячейках.
Код
Вот подкласс 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(...)
Ваш конкретный код не работает, потому что он создает новые UILabels в EventCell.layoutSubviews. Только начальные UILabels добавляются в дерево представлений, и их размеры, вероятно, равны 0.
Я предполагаю, что вы хотели обновить их фреймы, например, обновить метод класса 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>)
}
Вы можете использовать ленивую реализацию в swift, чтобы избежать необходимости заходить в 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)
}
}