为什么 collectionView 单元格的布局在应该相同时会有所不同?

Why is layout of collectionView cell diverse when it is supposed to be the same?

当我创建我的集合视图时,我将单元格定义为相同的布局

class FriendsController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    fileprivate let cellId = "cellId"
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Recent"
        collectionView?.backgroundColor = UIColor.white
        collectionView?.register(FriendCell.self, forCellWithReuseIdentifier: cellId)
        collectionView?.alwaysBounceVertical = true
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 2
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        return collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: view.frame.width, height: 100)
    }


}

class FriendCell: BaseCell {

    override func setupViews() {

        addSubview(profileImageView)
        addSubview(dividerLineView)
        profileImageView.image = UIImage(named: "zuckprofile")

        setupContainerView()

        addConstraintsWithFormat( "H:|-12-[v0(68)]|", views: profileImageView)
        addConstraintsWithFormat( "V:[v0(68)]", views: profileImageView)
        addConstraint(NSLayoutConstraint(item: profileImageView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
        addConstraintsWithFormat( "H:|-82-[v0]|", views: dividerLineView)
        addConstraintsWithFormat( "V:[v0(1)]|", views: dividerLineView)



    }
    func setupContainerView() {
        let containerView = UIView()
        addSubview(containerView)

        addConstraintsWithFormat("H:|-90-[v0]|", views: containerView)
        addConstraintsWithFormat(  "V:[v0(50)]", views: containerView)
        addConstraint(NSLayoutConstraint(item: containerView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
        containerView.addSubview(nameLabel)
        containerView.addSubview(messageLabel)
        containerView.addSubview(timelabel)
        containerView.addConstraintsWithFormat( "H:|[v0][v1]|", views: nameLabel, timelabel )
        containerView.addConstraintsWithFormat( "V:|[v0][v1(24)]|", views: nameLabel, messageLabel)
        containerView.addConstraintsWithFormat( "H:|[v0]-12-|", views: messageLabel )
        containerView.addConstraintsWithFormat("V:|[v0(20)]|", views: timelabel)
    }

}

extension UIView {
    func addConstraintsWithFormat(_ format: String , views: UIView...) {
        var viewsDictionary = [String: UIView]()
        for (index, view) in views.enumerated() {
            let key = "v\(index)"
            viewsDictionary[key] = view
            view.translatesAutoresizingMaskIntoConstraints = false
        }
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
    }
}

因此,从代码看来,单元格中的所有内容都大同小异。具有相同的布局、约束等 然后我得到了这个可怕的结果。

也许有人知道为什么这里会发生这种行为?

我认为问题在于 nameLabeltimeLabel 固有大小及其内容拥抱优先级和内容压缩阻力优先级之间的歧义(检查 this article)。

你有两个选择,在setupContainerView中为两个标签设置跟随水平压缩阻力和拥抱优先级:

nameLabel.setContentHuggingPriority(UILayoutPriority.defaultLow, for: .horizontal)
nameLabel.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: .horizontal)
timeLabel.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .horizontal)
timeLabel.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)

这将使 timeLabel 根据其内容保持大小,而 nameLabel 将占用其余内容(意味着如果未设置时间,timeLabel会缩小到宽度0,剩下的会被nameLabel).

占用

或者为这些标签之一设置明确的宽度,例如:

timeLabel.widthAnchor.constraint(equalToConstant: 40).isActive = true

编辑

根据Apple docs

When an ambiguous layout occurs at runtime, Auto Layout chooses one of the possible solutions to use. This means the layout may or may not appear as you expect. Furthermore, there are no warnings written to the console, and there is no way to set a breakpoint for ambiguous layouts.

other doc:

When stretching a series of views to fill a space, if all the views have an identical content-hugging priority, the layout is ambiguous. Auto Layout doesn’t know which view should be stretched.

None 我能找到的文档告诉我们,自动布局是如何解决这个问题的——我相信原因是为了确保我们不会依赖这个,而是明确地设定优先次序。因此,被拉伸的标签(甚至拉伸到的尺寸)实际上可以由任何随机算法选择。根据您的结果,我们最多可以得出结论,这可能不是基于将标签添加为子视图的顺序。