user3546621 user3546621 - 5 months ago 28
Swift Question

Custom cell with dynamic subviews stack

I have a tableView populated by an array of 'Post'. On this tableView I register a class 'PostCell'. This PostCell isn't supported by any nib since I want to dynamically compose it with subviews.

Suppose two subviews, a top yellowView and a bottom redView, each subclass of UIView, are stacked on a PostCell.

My issue is that the below code returns this:

enter image description here

ViewController

override func viewDidLoad(){
super.viewDidLoad()
tableView.delegate = self
tableView.datasource = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80
tableView.registerClass(PostCell.self, forCellReuseIdentifier: "PostCell")
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("PostCell") as! PostCell
cell.configureForData(post)
return cell
}


PostCell

class PostCell: UITableViewCell {

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

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

fun configureForData(post: Post){
let yellowView: UIView = UIView(frame: CGRect(x: 0, y: 0, width: contentView.frame.width, height: 40))
yellowView.backgroundColor = UIColor.yellowColor()
yellowView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(yellowView)

let redView: UIView = UIView(frame: CGRect(x: 0, y: 40, width: contentView.frame.width, height: 40))
redView.backgroundColor = UIColor.redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(redView)
}

override func layoutSubviews() {
super.layoutSubviews()
let height = NSLayoutConstraint(item: contentView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 80)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraint(height)
}
}


EDIT 1: This fixes the layout

class PostCell: UITableViewCell {

let yellowView = UIView()
let redView = UIView()

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

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

fun configureForData(post: Post){
yellowView.backgroundColor = UIColor.yellowColor()
yellowView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(yellowView)

redView.backgroundColor = UIColor.redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(redView)

layoutIfNeeded
}

override func layoutSubviews() {
super.layoutSubviews()
let height = NSLayoutConstraint(item: contentView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 80)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraint(height)

yellowView.frame = UIView(frame: CGRect(x: 0, y: 0, width: contentView.frame.width, height: 40))
redView.frame = UIView(frame: CGRect(x: 0, y: 40, width: contentView.frame.width, height: 40))
}

Answer

Try to use cell.layoutIfNeeded() after cell.configureForData(post) in your tableView:cellForRowAtIndexPath function. But I think it's better to calculate cell height and return it in tableView:heightForRowAtIndexPath function.

Also you have problem with cell width:

It's because you got contentView.frame.width before id correctly laid out. You need to update frames in each layoutSubviews call or use constraints.