UICollectionView - 重新选择项目时出现约束警告

UICollectionView - constraints warning when I reselect item

提问人:Martin Perry 提问时间:11/8/2023 更新时间:11/9/2023 访问量:26

问:

我有自定义 UICollectionView

class MyCollectionViewCell: UICollectionViewCell {

    var v0: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage()
        iv.contentMode = .scaleAspectFit
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    
    var v1: UILabel = {
        let lb = UILabel()
        lb.font = UIFont.systemFont(ofSize: 16, weight: .regular)
        lb.textAlignment = .center
        lb.backgroundColor = .clear
        lb.translatesAutoresizingMaskIntoConstraints = false
        return lb
    }()
    
    var v2: UILabel = {
        let lb = UILabel()        
        lb.font = UIFont.systemFont(ofSize: 16, weight: .regular)
        lb.textAlignment = .center
        lb.backgroundColor = .clear
        lb.clipsToBounds = true
        lb.translatesAutoresizingMaskIntoConstraints = false
        return lb
    }()
     
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addViews()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addViews()
    }
            
    private func addViews() {
                        
        let stack = UIStackView(arrangedSubviews: [v0, v1, v2])
        stack.distribution = .equalSpacing
        stack.alignment = .fill
        stack.axis = .vertical
        stack.translatesAutoresizingMaskIntoConstraints = false
        
        self.addSubview(stack)
        
        NSLayoutConstraint.activate([
                        
            v0.heightAnchor.constraint(equalToConstant: 24),
            v0.widthAnchor.constraint(equalTo: v0.heightAnchor, multiplier: 1.5),
            
            v1.widthAnchor.constraint(equalTo: v1.heightAnchor),
            v2.widthAnchor.constraint(equalTo: v2.heightAnchor),
            
            stack.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
            stack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),
            stack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
            stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)
        ])
    }
}

我在标准 UICollectionView 和 UICollectionViewFlowLayout 中使用它。

let layout = HorizontalLayout()
layout.minimumInteritemSpacing = 2
layout.minimumLineSpacing = 2
layout.scrollDirection = .horizontal
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

首次显示视图时,没有警告。但是,当我手动选择单元格时

colView.reloadData()
colView.layoutIfNeeded()
colView.selectItem(at: IndexPath(row: activeIndex, section: 0),
                   animated: false,
                   scrollPosition: .centeredHorizontally)

我从单元格中得到了很多约束警告,这些警告几乎在每个约束上都抱怨。

例如:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x2833edc20 UIImageView:0x12a06e580.height == 24   (active)>",
    "<NSLayoutConstraint:0x2833edc70 UIImageView:0x12a06e580.width == 1.5*UIImageView:0x12a06e580.height   (active)>",
    "<NSLayoutConstraint:0x2833eddb0 H:|-(0)-[UIStackView:0x12a06ed20]   (active, names: '|':app.MyCollectionViewCell:0x12a06e310 )>",
    "<NSLayoutConstraint:0x2833ede00 UIStackView:0x12a06ed20.trailing == app.MyCollectionViewCell:0x12a06e310.trailing   (active)>",
    "<NSLayoutConstraint:0x2833ee670 'UISV-canvas-connection' UIStackView:0x12a06ed20.leading == UIImageView:0x12a06e580.leading   (active)>",
    "<NSLayoutConstraint:0x2833ee490 'UISV-canvas-connection' H:[UIImageView:0x12a06e580]-(0)-|   (active, names: '|':UIStackView:0x12a06ed20 )>",
    "<NSLayoutConstraint:0x2833d4b90 'UIView-Encapsulated-Layout-Width' app.MyCollectionViewCell:0x12a06e310.width == 50   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x2833edc20 UIImageView:0x12a06e580.height == 24   (active)>

出了什么问题,为什么只有手动选择后才出现问题?

iOS Swift 自动布局

评论

0赞 Teja Nandamuri 11/8/2023
我认为当图像与其他标签位于垂直堆栈中时,您不应该对图像进行宽度约束,这会使堆栈视图宽度变得模棱两可。

答:

1赞 DonMag 11/9/2023 #1

首先,当使用 a (和使用 a ) 时,所有子视图都应添加到单元格的 :UICollectionViewCellUITableViewCellcontentView

//self.addSubview(stack)
contentView.addSubview(stack)

和:

//stack.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
//stack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),
//stack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
//stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)

stack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
stack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
stack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0)

接下来,为了避免调试控制台中的自动布局投诉,您需要为 Flow 布局提供合理的.estimatedItemSize

您显示的布局为您提供了一个图像视图,以及垂直堆栈视图中的两个标签...因此,我们可以使用准确的尺寸:36 x 2436 x 36

layout.estimatedItemSize = .init(width: 36.0, height: 96.0)

现在,您可以在不收到错误/警告消息的情况下进行呼叫。.selectItem(at: ...)


编辑 - 稍微澄清一下......

使用“自动调整大小”单元格很常见 - 可能比固定大小的单元格更常见。

滚动浏览集合视图时,自动布局会在创建/重用单元格时处理对单元格 UI 元素的约束。通常,只要约束设置正确,一切都会按预期工作。

但是,在执行“跳转到”单元格的代码时,自动布局使用 .estimatedItemSize 进行初始框架计算。我们可以将其视为对自动布局的“提示”。

UICollectionViewFlowLayout.automaticSize等于:

CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)

CGSize(width: 1.7976931348623157e+308, height: 1.7976931348623157e+308)

在这种特定情况下,单元格的宽度都相等(我们知道宽度),我们可以指定一个确切的宽度.estimatedItemSize

layout.estimatedItemSize = .init(width: 36.0, height: 96.0)

以避免投诉。

UIKit 在管理集合视图等元素方面做了很多工作......在调试控制台中看到这些类型的错误/警告消息并不罕见。只要您了解生成它们的原因,并且您的代码正在生成所需的布局,您就可以安全地忽略这些警告。

评论

0赞 Martin Perry 11/9/2023
为什么必须将大小显式分配给估计大小?约束非常明确,系统应该能够计算它。它甚至确实如此,当我第一次运行它时,没有任何警告......问题仅在 selectItem 调用之后。
0赞 DonMag 11/9/2023
@MartinPerry - 请参阅编辑我的答案以获得更多说明。