TL;DR
When trying to size UICollectionViewCells via auto layout, you can easily get auto layout warnings with even a simple example.
Should we be setting
After testing things out, I noticed at least one reason to keep contentView.translatesAutoresizingMaskToConstraints = true
.
If you use preferredLayoutAttributesFittingAttributes(_:)
to alter the dimensions of a UICollectionViewCell
, it works by sizing the contentView to the correct dimension. If you set contentView.translatesAutoresizingMaskToConstraints = false
, you'll lose this functionality.
Therefore, I recommend using Solution 1 (altering at least one constraint in each dimension to be non-required). In fact, I created a wrapper for UICollectionViewCell that will handle both the required 999 constraint, and a way to get preferred height or width to function correctly.
By using this wrapper, you won't need to remember the intricacies of getting the contentView in a UICollectionViewCell to behave properly.
class CollectionViewCell<T where T: UIView>: UICollectionViewCell {
override init(frame: CGRect) {
preferredHeight = nil
preferredWidth = nil
super.init(frame: frame)
}
var preferredWidth: CGFloat?
var preferredHeight: CGFloat?
private(set) var view: T?
func initializeView(view: T) {
assert(self.view == nil)
self.view = view
contentView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
var constraint: NSLayoutConstraint
constraint = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant: 0)
constraint.active = true
constraint = NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: contentView, attribute: .Leading, multiplier: 1, constant: 0)
constraint.active = true
// Priority must be less than 1000 to prevent errors when installing
// constraints in conjunction with the contentView's autoresizing constraints.
let NonRequiredPriority: UILayoutPriority = UILayoutPriorityRequired - 1
constraint = NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant: 0)
constraint.priority = NonRequiredPriority
constraint.active = true
constraint = NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: contentView, attribute: .Trailing, multiplier: 1, constant: 0)
constraint.priority = NonRequiredPriority
constraint.active = true
}
override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let newLayoutAttributes = super.preferredLayoutAttributesFittingAttributes(layoutAttributes)
if let preferredHeight = preferredHeight {
newLayoutAttributes.bounds.size.height = preferredHeight
}
if let preferredWidth = preferredWidth {
newLayoutAttributes.bounds.size.width = preferredWidth
}
return newLayoutAttributes
}
}
(Note: the init method is required due to a bug with generic subclasses of UICollectionViewCell.)
To register:
collectionView.registerClass(CollectionViewCell<UIView>.self, forCellWithReuseIdentifier: "Cell")
To use:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CollectionViewCell<UILabel>
if cell.view == nil {
cell.initializeView(UILabel())
}
cell.view!.text = "Content"
cell.preferredHeight = collectionView.bounds.height
return cell
}