Peter Suwara
Peter Suwara

Reputation: 816

Adding a view loaded from a NIB to a Custom Table View Cell doesn't work as expected

I have been playing around with reusing views inside a table view cell. I have a custom table view cell and another reusable view that I use in the cell and in other parts of my app. The reusable view is defined in a XIB file and linked accordingly with it's class definition.

I am having strange behaviour when I try to get the view inside a custom table view cell

DevicesTableViewCell <- Custom UITableViewCell to hold a view that is in another NIB.

DeviceView <- Custom reusable view that can be used in a cell and in other parts of the app, like a Stack View.

Now, when I try to load the nib and add it to the Cells Content View, the view doesn't appear. However if I try to add it to a container view inside the table view cell, it does work.

Cell setup code :

    let devicesName         = sectionDataArray[MasterSection.devices.rawValue].reuseId
    let devicesNib          = UINib(nibName: devicesName, bundle: nil)
    tableView.register(devicesNib, forCellReuseIdentifier: devicesName)
  1. Code where view does not appear :

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: sectionDataArray[indexPath.section].reuseId)
    
    print ("-- Showing devices cell")
    let deviceCell = cell as! DevicesTableViewCell
    
    // Optimise this, we way not necessarily need to load this view every single time
    if deviceCell.deviceView == nil {
        deviceCell.deviceView = Bundle.main.loadNibNamed("DeviceView", owner: self, options: nil)?.first as! DeviceView
        deviceCell.containerView.isHidden = true
        deviceCell.contentView.addSubview(deviceCell.deviceView)
    
        let views = ["deviceView" : deviceCell.deviceView]
    
        deviceCell.deviceView.translatesAutoresizingMaskIntoConstraints = false
    
        deviceCell.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[deviceView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
        deviceCell.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[deviceView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
    }
    
    // TODO - Setup cell data contents
    
    return deviceCell
    }
    
  2. Code where view does appear :

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: sectionDataArray[indexPath.section].reuseId)
    
    print ("-- Showing devices cell")
    let deviceCell = cell as! DevicesTableViewCell
    
    // Optimise this, we way not necessarily need to load this view every single time
    if deviceCell.deviceView == nil {
        deviceCell.deviceView = Bundle.main.loadNibNamed("DeviceView", owner: self, options: nil)?.first as! DeviceView
        //deviceCell.containerView.isHidden = true
        deviceCell.containerView.addSubview(deviceCell.deviceView)
    
        let views = ["deviceView" : deviceCell.deviceView]
    
        deviceCell.deviceView.translatesAutoresizingMaskIntoConstraints = false
    
        deviceCell.containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[deviceView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
        deviceCell.containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[deviceView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
    }
    
    // TODO - Setup cell data contents
    
    return deviceCell
    }
    

Custom Cell Code :

class DevicesTableViewCell: UITableViewCell {

@IBOutlet weak var containerView: UIView!
var deviceView: DeviceView!

override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}

}

RESULT:

  1. Adding to contentView -> Nothing appears but a white background.

  2. Adding to containerView -> The view appears as normal and is contained in the cell with the correct auto layout constraints internally.

NOTES: The containerView is in the Custom Table Cell View XIB file. Container view has autolayout constraints bounding its view to the table view cell view with margins of 0.

QUESTION: Can anyone explain why in this particular case the Content view does not correctly display its subviews ?

Upvotes: 1

Views: 1795

Answers (1)

Peter Suwara
Peter Suwara

Reputation: 816

The problem was that I had the wrong object inside the XIB file.

Inside the DevicesTableViewCell.xib, the view created was a UIView object. Not a UITableViewCell object.

If you instantiate a cell from a XIB, make sure that view object is created from a UITableViewCell.

This is because the UITableViewCell object has a content view wrapped inside it.

After changing the XIB file adding objects to the content view worked correct.

Upvotes: 2

Related Questions