AutoLayout on UITableViewCell subclass breaks after reuse

663 Views Asked by At

I have a subclass of UITableViewCell that uses AutoLayout and is reused through UITableView registerNib(nib: UINib, forCellReuseIdentifier: String) and dequeueReusableCellWithIdentifier(identifier: String). The cell has two buttons and I set the AutoLayout constraints on layoutSubviews():

override func layoutSubviews()
{
    super.layoutSubviews()

        //Clear all constraints from the cell
        self.contentView.removeConstraints(self.contentView.constraints())
        self.oneButton.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.twoButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        let viewsDict : [NSObject: AnyObject] = ["oneButton" : self.oneButton, "twoButton": self.twoButton]

        //Setting the constraints
        self.contentView.addConstraint(NSLayoutConstraint(item: self.oneButton, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.twoButton, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0))
        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[oneButton]-25-[twoButton]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-25-[oneButton(40)]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-25-[twoButton(40)]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
}

This works only for the first time the cell is instantiated. After it is reused the horizontal spacing and sizing breaks. After playing around and setting breakpoints I found out that updateConstraints is called after layoutSubviews and only the first time the cell is instantiated, not after reused.

After a little bit more fiddling around I tried this:

override func updateConstraints()
{
    super.updateConstraints()
    self.hasUpdated = true
}

override func layoutSubviews()
{
    super.layoutSubviews()

    if (!self.hasUpdated)
    {
        //Clear all constraints from the cell
        self.contentView.removeConstraints(self.contentView.constraints())
        self.oneButton.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.twoButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        let viewsDict : [NSObject: AnyObject] = ["oneButton" : self.oneButton, "twoButton": self.twoButton]

        //Setting the constraints
        self.contentView.addConstraint(NSLayoutConstraint(item: self.oneButton, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.twoButton, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0))
        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[oneButton]-25-[twoButton]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-25-[oneButton(40)]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-25-[twoButton(40)]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
    }
}

And now it works as expected, but I feel that I'm maybe inhibiting some reuse layout mechanic, since I'm not always setting the constraints. Any thoughts about this?

EDIT: After Shai comment down bellow, I thought that the removeConstraints and setTranslatesAutoresizingMaskIntoConstraints calls could be the culprits. What I did then was stop overriding layoutSubviews and changing it to:

override func awakeFromNib()
{
    super.awakeFromNib()

    self.contentView.removeConstraints(self.contentView.constraints())
    self.oneButton.setTranslatesAutoresizingMaskIntoConstraints(false)
    self.twoButton.setTranslatesAutoresizingMaskIntoConstraints(false)
    // Initialization code
}

override func updateConstraints()
{
    super.updateConstraints()

    let viewsDict : [NSObject: AnyObject] = ["oneButton" : self.oneButton, "twoButton": self.twoButton]

    //Setting the constraints
    self.contentView.addConstraint(NSLayoutConstraint(item: self.oneButton, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.twoButton, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0))
    self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[oneButton]-25-[twoButton]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
    self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-25-[oneButton(40)]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
    self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-25-[twoButton(40)]-25-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
}

And now I don't feel like hacking reuse mechanics... Still, any thoughts on this?

0

There are 0 best solutions below