Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a programmatically created UITableViewCell subclass in Swift

I'm working on a table view with a custom cell that has been created programmatically, with no usage of IB. I've looked around on Google and asked around in NSChat, but I still can't get it to work. It just displays the default table view. Thanks in advance!

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       } } 
like image 435
Jeffrey Avatar asked Dec 20 '14 23:12

Jeffrey


2 Answers

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.

like image 87
drewag Avatar answered Oct 10 '22 05:10

drewag


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

Here is the view controller code.

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

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(...)
like image 40
Suragch Avatar answered Oct 10 '22 05:10

Suragch