Using a programmatically created UITableViewCell subclass in Swift
Ok first a few important comments.
First, you are needlessly creating your own new cell every time the table view requests a cell instead of reusing old cells. You should remove this line:
cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier)
That is unnecessary because dequeue will automatically create new cells as needed based on the class you have registered for the given identifier.
Second, you should not be using the main screen bounds when laying out your code. This will break down if your table view is not the full width. Instead, you can use self.bounds
so it is always relative to the cell itself.
Third, you should not be calling setNeedsLayout or layoutIfNeeded because if that method is called, it is already laying out everything again.
Fourth, you should register your table view cell class before setting the table view data source just in case UITableView every starts requesting things from data source when the data source is set.
Fifth, two of your subviews have a size of 0,0 so they are not going to show up anyway.
If this code is indeed running without crashing then you are creating EventCells because you are doing a forced casting from the result of the dequeueReusableCellWithIdentifier:forIndexPath
. That means you simply have a layout / display / data issue.
I had the same question. I applied the advice in @drewag's answer, but there were some additional issues. Below is a bare-bones, proof-of-concept example that shows how to use a programmatically created UITableViewCell
subclass.
The image below is what it should look like. The yellow rectangles are UILabel
subviews in the custom cells.
Code
Here is the UITableViewCell subclass. It's job is to initialize the cell and its content, add the subviews, and layout the subviews.
import UIKitclass 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) }}
Here is the view controller code.
import UIKitclass 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 }}
Notes:
- I used a regular
UIViewController
rather than aUITableViewController
. If you use aUITableViewController
then remove theUITableViewDelegate
andUITableViewDataSource
protocol references since these are redundant for aUITableViewController
. You also won't need the@IBOutlet tableView
line. - The main problem line I found in the original question was
eventName = UILabel(frame: CGRectMake(...))
. Instead it should have beeneventName.frame = CGRect(...)
Your particular code does not work because it is creating new UILabels in EventCell.layoutSubviews. Only the initial UILabels are getting added to the view tree and their sizes are probably 0'd.
I'm guessing you meant to update their frames, e.g. update the EventCell class method:
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>)}