使用程序代码创建UITableViewCell的约束条件

4

这一切都发生在这个类内部:

class CustomCell2: UITableViewCell {

当我打印在Storyboard中设置的现有宽度约束(标签=单元格宽度的0.7倍)的值时,我看到了这个:

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

当我尝试以编程方式创建相同的约束时,会出现错误:“视图层次结构未准备好约束:”。
<NSLayoutConstraint:0xee08590 UILabel:0x3d2aac0'Label'.width == 0.9*appName.CustomCell2:0x3e01880'CustomCell2'.width>

看起来完全相同,那么为什么不能添加约束?

CustomCell2类中awakeFromNib()方法中的约束代码:

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

错误信息的其余部分:

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>>

谢谢!

3个回答

7

假设您将使用可重用的dequeued单元格,您应该在init中添加约束条件,记得使用contentView而不是View来获取单元格的边界:

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 

//...

我喜欢使用以下方法来替代使用硬编码的值/字符串。
/// 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)

非常感谢您的参与!添加override init会使Xcode出现错误“需要init(coder :)初始化程序”,并且在添加init(coder :)之后,init函数从未被调用以运行addSubViewsAndLayout调用。您是正确的,它是tableName.dequeueReusableCell(withIdentifier: "CustomCell2") as! CustomCell2。awakeFromNib被调用,但不是init。 - RanLearns
1
实现程序员对super的调用:public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - RLoniello
1
还要确保在视图加载或故事板中注册单元格。 - RLoniello
谢谢!注册单元格触发了init - 你知道在addSubViewsAndLayout函数中哪个部分可能会失败,导致出现“在解包可选值时意外地发现nil”的错误吗?titleLabel.widthAnchor.constraint(equalTo: self.contentView.widthAnchor, multiplier: 0.9, constant: 0).isActive = true - RanLearns
抱歉打扰了,但我在其他地方看到调用register实际上会使单元格不从storyboard中获取,这会导致IBOutlets为nil:https://dev59.com/questions/FITba4cB1Zd3GeqP4ld3#26411483 - RanLearns
显示剩余8条评论

1

一个苹果开发者论坛上的用户指引我找到了正确的方向。约束条件是在标签和单元格(在自定义单元格类中被称为“self”)之间的,答案是我必须将约束添加到单元格而不是标签。完全相同的约束定义,但只有当编码为self.addConstraint()时才起作用,并在编码为labelName.addConstraint()时出现错误“视图层次结构未准备好进行约束”。


1
您可以通过编程的方式创建UITableViewCell的约束,例如:
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
}  
}

定义模型:
struct CellModel {
let labelString : String
}

定义自定义单元格:
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")
} 
}

输出


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接