Using a programmatically created UITableViewCell subclass in Swift Using a programmatically created UITableViewCell subclass in Swift ios ios

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.

enter image description here

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 a UITableViewController. If you use a UITableViewController then remove the UITableViewDelegate and UITableViewDataSource protocol references since these are redundant for a UITableViewController. 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 been eventName.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>)}