NSLayoutConstraints 在 UITableViewCell 中不受尊重

NSLayoutConstraints is not respected in UITableViewCell

我有一个 UITableViewCell,一个 UILabel 和一个 UIImageView。图像可以是可见的或隐藏的。

这是我的故事板:

UILabel 的尾部有两个约束,一个 (a) 等于 8 与 UIImageView,另一个 (b) 大于或等于 8 与单元格的右边距。我保留第一个 (a) 的参考,如果有或没有声音,我会激活或停用约束。

这是我的代码:

class MyTableViewCell: UITableViewCell {

    @IBOutlet weak var label: UILabel?
    @IBOutlet weak var icon: UIImageView?
    @IBOutlet weak var spaceBetweenIconAndLabelConstraint: NSLayoutConstraint?

    override func awakeFromNib() {
        super.awakeFromNib()
        icon?.image = UIImage(named: "sound")
    }

    func config(with name: String, hasSound: Bool) {
        label?.text = name
        configSound(hasSound)
    }

    private func configSound(_ hasSound: Bool) {
        icon?.isHidden = !hasSound
        spaceBetweenIconAndLabelConstraint?.isActive = hasSound
    }
}

我有几个单元格可以看到声音图标,很多没有。这是特定单元格首次出现时的样子:

以及它再次出现在屏幕上时的样子:

我知道问题出在重复使用的电池上。但我不明白如何防止这种行为。我尝试这样做:

override func prepareForReuse() {
    configSound(true)
}

在重新使用单元格之前重新激活约束,但它不起作用。

我认为问题在于您对约束使用了 weak 引用。在这种情况下,一旦 isActive 属性 首次设置为 false,约束就会被删除。从那以后它是 nil 并且不能被重新激活。

解决方案:通过删除 weak 关键字来使用强引用。

@IBOutlet var spaceBetweenIconAndLabelConstraint: NSLayoutConstraint!

有两种以上的方法可以做到。如果您的目标是 iOS 9+,我强烈建议您使用堆栈视图。它们完全满足您的需求,无需手动 add/remove/activate/deactivate 约束。

UI 看起来像这样:

stack view setup

水平堆栈视图(前导 8,尾随 8,间距等于 8) 里面: 1.在左边的标签上 2. 右侧图标图像视图(可选择包装在 iconContainer 视图中,或仅设置 aspectFit)

更新代码:

class MyTableViewCellWithStackView: UITableViewCell {

    @IBOutlet weak var label: UILabel?
    @IBOutlet weak var iconContainer: UIView?

    func config(with name: String, hasSound: Bool) {
        label?.text = name
        iconContainer?.isHidden = !hasSound
    }
}

每当您隐藏 icon/iconContainer 时,堆栈视图将自行更新并相应地填充 space。

如果你不能使用堆栈视图(首选),你可以试试这个:

class MyTableViewCell: UITableViewCell {

    @IBOutlet weak var label: UILabel?
    @IBOutlet weak var icon: UIImageView?
    @IBOutlet weak var spaceBetweenIconAndLabelConstraint: NSLayoutConstraint?

    override func awakeFromNib() {
        super.awakeFromNib()
        icon?.image = UIImage(named: "sound")
    }

    func config(with name: String, hasSound: Bool) {
        label?.text = name
        configSound(hasSound)
    }

    private func configSound(_ hasSound: Bool) {
        icon?.isHidden = !hasSound
        guard hasSound else {
            spaceBetweenIconAndLabelConstraint?.isActive = false
            return
        }
        guard let icon = icon, let label = label else { return }

        let constraint = label.rightAnchor
                                .constraint(equalTo: icon.leftAnchor, constant: 8)
        constraint.isActive = true
        spaceBetweenIconAndLabelConstraint = constraint
    }
}