I have three custom views side by side in one line. If the content of the last view does not fit (as can be seen in the picture), move that view to a new line. How?
These is constraints:
firstCustomView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
secondCustomView.leadingAnchor.constraint(equalTo: firstCustomView.trailingAnchor, constant: 15),
thirdsCustomView.leadingAnchor.constraint(equalTo: secondCustomView.trailingAnchor, constant: 15),
thirdsCustomView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -15),
CodePudding user response:
I did it in this way, first create custom collection view class:
class DynamicHeightCollectionView: UICollectionView {
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
return contentSize
}
}
Second, create custom layout:
open class UICollectionViewLeftAlignedLayout: UICollectionViewFlowLayout {
open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return super.layoutAttributesForElements(in: rect)?.map { $0.representedElementKind == nil ? layoutAttributesForItem(at: $0.indexPath)! : $0 }
}
open override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let currentItemAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes,
collectionView != nil else {
return nil
}
if scrollDirection == .vertical {
if indexPath.item != 0,
let previousFrame = layoutAttributesForItem(at: IndexPath(item: indexPath.item - 1, section: indexPath.section))?.frame,
currentItemAttributes.frame.intersects(CGRect(x: -.infinity, y: previousFrame.origin.y, width: .infinity, height: previousFrame.size.height)) {
currentItemAttributes.frame.origin.x = previousFrame.origin.x previousFrame.size.width evaluatedMinimumInteritemSpacingForSection(at: indexPath.section)
} else {
currentItemAttributes.frame.origin.x = evaluatedSectionInsetForSection(at: indexPath.section).left
}
} else {
if indexPath.item != 0,
let previousFrame = layoutAttributesForItem(at: IndexPath(item: indexPath.item - 1, section: indexPath.section))?.frame,
currentItemAttributes.frame.intersects(CGRect(x: previousFrame.origin.x, y: -.infinity, width: previousFrame.size.width, height: .infinity)) {
currentItemAttributes.frame.origin.y = previousFrame.origin.y previousFrame.size.height evaluatedMinimumInteritemSpacingForSection(at: indexPath.section)
} else {
currentItemAttributes.frame.origin.y = evaluatedSectionInsetForSection(at: indexPath.section).top
}
}
return currentItemAttributes
}
func evaluatedMinimumInteritemSpacingForSection(at section: NSInteger) -> CGFloat {
return (collectionView?.delegate as? UICollectionViewDelegateFlowLayout)?.collectionView?(collectionView!, layout: self, minimumInteritemSpacingForSectionAt: section) ?? minimumInteritemSpacing
}
func evaluatedSectionInsetForSection(at index: NSInteger) -> UIEdgeInsets {
return (collectionView?.delegate as? UICollectionViewDelegateFlowLayout)?.collectionView?(collectionView!, layout: self, insetForSectionAt: index) ?? sectionInset
}
}
And at initialization:
@IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.dataSource = self
collectionView.delegate = self
collectionView.isScrollEnabled = false
collectionView.register(UINib(nibName: "BubbleCell", bundle: nil), forCellWithReuseIdentifier: "BubbleCell")
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = data[indexPath.row].name.size(withAttributes:[.font: UIFont(name: "Lato-Regular", size: 16)!])
let width = min(size.width 52, collectionView.frame.width - collectionView.contentInset.left - collectionView.contentInset.right)
return CGSize(width: width, height: 34)
}