Michael Voccola
Michael Voccola

Reputation: 1835

Subclassing a UITableViewCell Subclass

The goal is to use a common XIB file to display a variety of objects. To do so, I have created the XIB and connected it's IBOutlets to the UITableView subclass, MyCustomCell

class MyCustomCell:UITableViewCell {
    @IBOutlet weak var myLabel:UILabel!   
}

This cell should be capable of displaying data from two types of objects and it enhances convenience to have the cell handle it's own population rather than access myLabel from the tableViewDelegate, so two separate subclasses are needed:

class ObjectTypeATableViewCell:MyCustomCell {

    var record:ObjectTypeA! {
        didSet {
            myLabel.text = record.name
        }
    }
}

class ObjectTypeBTableViewCell:MyCustomCell {
    var record:ObjectTypeB! {
        didSet {
            myLabel.text = String(record.totalQty)
        }
    }
}

It all builds fine. The issue, however, comes with reusing the cell on the TableView. Below is the implementation of this I am working with.

class MyTable: UITableView, UITableViewDataSource {

    var records:Array<AnyObject>()

    /// Run when the tableView is loaded
    func xibSetup() {
        let nib = UINib(nibName: "MyCustomCell", bundle: nil)
        registerNib(nib, forCellReuseIdentifier: "recordCell")
        dataSource = self
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let object = sectionedItems().itemsInSections[indexPath.section][indexPath.row]
        if let i = object as? ObjectTypeA {
            let cell = dequeueReusableCellWithIdentifier("recordCell") as! ObjectTypeATableViewCell
            cell.record = i
            return cell
        } else if let i = object as? ObjectTypeB {
            let cell = dequeueReusableCellWithIdentifier("recordCell") as! ObjectTypeBTableViewCell
            cell.record = i
            return cell
        } else {
            let cell = UITableViewCell()
            cell.textLabel?.text = "unknown object type."
            return cell
        }
    }

}

A fatal error is occurring when the cell is dequeued:

Could not cast value of type 'MyProject.MyCustomCell' (0x10ade39e0) to 'MyProject. ObjectTypeATableViewCell' (0x10ade3e20).

How can the cell be properly cast as a subclass?

Upvotes: 0

Views: 1686

Answers (2)

Sua Le
Sua Le

Reputation: 137

Dont need to subclass MyCustomCell, you can declare a protocol to use like this:

protocol ObjectTypeProtocol {
    func getText() -> String
}

class ObjectTypeA : ObjectTypeProtocol{
    var name = "name of ObjectTypeA"
    //....
    func getText() -> String {
        return name
    }
}
class ObjectTypeB : ObjectTypeProtocol{
    var totalQty = "total of ObjectTypeB"
    //....
    func getText() -> String {
        return String(totalQty)
    }
}

class MyCustomCell: UITableViewCell {
    @IBOutlet var myLabel:UILabel!

    var record:ObjectTypeProtocol! {
        didSet {
            myLabel.text = record.getText()
        }
    }
}

Upvotes: 2

ZeroChow
ZeroChow

Reputation: 442

Try to change the tableView regist class like this :

tableView.registerClass(ObjectTypeATableViewCell.self, forCellReuseIdentifier: "ObjectTypeATableViewCell")

To solve your problem maybe you should creating another custom view instead, just named "MyCustomCellContentView", and it inherit from UIView, also create a xib file named "MyCustomCellContentView". Set the xib class "MyCustomCellContentView"

enter image description here

After the step, you should move views in MyCustomCell to MyCustomCellContentView, and then delete theMyCustomCell.xibfile. Link the label in MyCustomCellContentView. Change your code @IBOutlet weak var myLabel:UILabel! with var myLabel : UILabel?

Then override init(style: UITableViewCellStyle, reuseIdentifier: String?) in MyCustomerCell:

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

    let nib = NSBundle.mainBundle().loadNibNamed("MyCustomCellContentView",owner: nil, options: nil).first
    let view = nib as! MyCustomCellContentView

    myLabel = view.label

    contentView.addSubview(view)
}

At last, override layoutSubViews :

override func layoutSubviews() {
        super.layoutSubviews()
        label!.superview!.frame = self.bounds
}

Upvotes: 0

Related Questions