When i was creating my collection view, i defined cell to be the same with the same layout
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))
}
}
So as it seems from the code everything in cells shout be the same. With the same layout, constraints etc Then i got this terrible result.
Please maybe someone knows why this behavior take place here?
I believe the problem lies in ambiguity between nameLabel
and timeLabel
intrinsic size and their content hugging priority and content compression resistance priority (check this article).
You have two options, either set following horizontal compression resistance and hugging priority for both labels in 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)
Which will make the timeLabel
to keep the size according to its content and the nameLabel
will take the rest of the content (meaning that if the time is not set, the timeLabel
will be shrinked to width 0, and the rest will be taken up by nameLabel
).
Or set an explicit width for one of those labels, e.g.:
timeLabel.widthAnchor.constraint(equalToConstant: 40).isActive = true
EDIT
According to 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.
Or 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 of the docs I was able to find tells us, how does the Auto Layout solve this issues - and I believe the reason is to make sure we won't be relying on this and rather explicitly set the priorities. Therefore, the label that gets stretched (even the size to which it is stretched) can be really chosen by any random algorithm. Based from your result we can at best conclude, that probably it is not based on the order in which labels were added as subviews.