Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create UITableViewCell constraint programmatically

This is all taking place within this class:

class CustomCell2: UITableViewCell {

When I print the value of an existing width constraint (label = 0.7 * width of the cell) that was set in Storyboard, I see this:

<NSLayoutConstraint:0x36b8750 UILabel:0x3d2aac0'Label'.width == 0.7*appName.CustomCell2:0x3e01880'CustomCell2'.width>

When I try to create the same constraint programmatically, I get the error "The view hierarchy is not prepared for the constraint:"

<NSLayoutConstraint:0xee08590 UILabel:0x3d2aac0'Label'.width == 0.9*appName.CustomCell2:0x3e01880'CustomCell2'.width>

Seems exactly the same, so why can't that constraint be added?

Constraint code in awakeFromNib() of CustomCell2:

let widthConstraint = NSLayoutConstraint(item: labelName, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.width, multiplier: 0.9, constant: 0)
labelName.addConstraint(widthConstraint)

Rest of the error message:

When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
2017-12-03 20:18:51.183 appName[899:684382] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0xee08590 UILabel:0x3d2aac0'Label'.width == 0.9*appName.CustomCell2:0x3e01880'CustomCell2'.width>
    Container hierarchy: 
<UILabel: 0x3d2aac0; frame = (281 43; 674 54); text = 'Label'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x4233d30>>
    View not found in container hierarchy: <appName.CustomCell2: 0x3e01880; baseClass = UITableViewCell; frame = (0 0; 963 160); layer = <CALayer: 0xee08250>>
    That view's superview: NO SUPERVIEW
Unable to install constraint on view.  Does the constraint reference something from outside the subtree of the view?  That's illegal. constraint:<NSLayoutConstraint:0xee08590 UILabel:0x3d2aac0'Label'.width == 0.9*appName.CustomCell2:0x3e01880'CustomCell2'.width> view:<UILabel: 0x3d2aac0; frame = (281 43; 674 54); text = 'Label'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x4233d30>>

Thanks!

like image 606
RanLearns Avatar asked Oct 24 '25 15:10

RanLearns


2 Answers

you should be adding your constraints in init for the cell, assuming it will be a dequeued reusable cell, remember to use contentView instead of View for the bounds of the cell:

class CustomCell2: UITableViewCell {

//MARK: Subviews
//Add View Programmatically or in xib
let titleLabel: UILabel = {
    let label = UILabel()
    label.text = " Title "
    //…
    label.translatesAutoresizingMaskIntoConstraints = false //Must use
    return label
}()

//…


//MARK: init
//Add Subviews and then layout Contraints to the Cell’s contentView
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    addSubViewsAndlayout()
}



/// Add and sets up subviews with programmically added constraints
func addSubViewsAndlayout() {
    contentView.addSubview(titleLabel) //will crash if not added

    let screenwidth = UIScreen.main.bounds.width //get any other properties you need

    titleLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 12.0).isActive = true
    titleLabel.heightAnchor.constraint(equalToConstant: 30).isActive = true
    titleLabel.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 12).isActive = true
    titleLabel.rightAnchor.constraint(equalTo: otherViewToTheSide.rightAnchor, constant: -24).isActive = true 

//...

I also like to implement the following methods instead of using hard coded values / strings.

/// Get the Height of the cell
/// use this in heightForRowAt indexPath
/// - Returns: CGFloat
class func height() -> CGFloat {
    return 175
}
//CustomCell2.height()


/// Get the string identifier for this class.
///
/// - Retruns: String
class var identifier: String {
    return NSStringFromClass(self).components(separatedBy: ".").last!
}
//use when registering cell:
self.tableView.register(CustomCell2.self, forCellReuseIdentifier: CustomCell2.identifier)
like image 72
RLoniello Avatar answered Oct 26 '25 05:10

RLoniello


You can create UITableViewCell constraint Programatically like :

class ViewController: UITableViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(CustomCell2.self, forCellReuseIdentifier: "cell")
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 2
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? CustomCell2 else { return UITableViewCell() }
    cell.model = CellModel(labelString: "set constriant by code")
    return cell
}  
}

Define Model :

struct CellModel {
let labelString : String
}

Define custom Cell :

class CustomCell2 : UITableViewCell {
private let label : UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false // enable auto-layout
    label.backgroundColor = .green // to visualize 
    label.textAlignment = .center // text alignment in center
    return label
}()

private func addLabel() {
    addSubview(label)
    NSLayoutConstraint.activate([
        // label width is 70% width of cell
        label.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.7),
        // center along horizontally 
        label.centerXAnchor.constraint(equalTo: centerXAnchor)
    ])
}

var model : CellModel? {
    didSet {
        label.text = model?.labelString ?? "Test"
    }
}

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

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
} 
}

Output

like image 22
Ashis Laha Avatar answered Oct 26 '25 04:10

Ashis Laha



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!