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!
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With