У меня примерно такая же функциональность, поэтому я делюсь тем, что сделал, в рабочем состоянии. Если ваше требование немного отличается, вам нужно выполнить дополнительную настройку.
Для объяснения кода я предлагаю вам go по ссылкам, упомянутым в последнем разделе ответа.
Создание пользовательского класса FlowLayout
class CustomLayout: UICollectionViewFlowLayout {
private var computedContentSize: CGSize = .zero
private var cellAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
var array = [String]()
override var collectionViewContentSize: CGSize {
return computedContentSize
}
override func prepare() {
computedContentSize = .zero
cellAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
var preY = 10
var highY = 10
for section in 0 ..< collectionView!.numberOfSections {
var i = 0
for item in 0 ..< collectionView!.numberOfItems(inSection: section) {
let awidth = Int((self.collectionView?.bounds.width)!) / 3 - 15
let aheight = self.array[item].height(withConstrainedWidth: CGFloat(awidth), font: UIFont.systemFont(ofSize: 16)) + 20
var aX = 10
if item % 3 == 1 {
aX = aX + 10 + awidth
}
else if item % 3 == 2 {
aX = aX + 20 + 2 * awidth
}
else {
aX = 10
preY = highY
}
if highY < preY + Int(aheight) {
highY = preY + Int(aheight) + 5
}
let itemFrame = CGRect(x: aX, y: preY, width: awidth, height: Int(aheight))
let indexPath = IndexPath(item: item, section: section)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = itemFrame
cellAttributes[indexPath] = attributes
i += 1
}
}
computedContentSize = CGSize(width: (self.collectionView?.bounds.width)!,
height: CGFloat(collectionView!.numberOfItems(inSection: 0) * 70))
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var attributeList = [UICollectionViewLayoutAttributes]()
for (_, attributes) in cellAttributes {
if attributes.frame.intersects(rect) {
attributeList.append(attributes)
}
}
return attributeList
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cellAttributes[indexPath]
}
}
MainViewController
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@IBOutlet weak var myColView: UICollectionView!
var array = ["Hello", "Look Behind", "Gotta get there", "Hey buddy", "Earth is rotating around the sun", "Sky is blue", "Kill yourself", "Humble docters", "Lets make party tonight Lets make party tonight", "Lets play PUB-G", "Where are you?", "Love you Iron Man."]
override func viewDidLoad() {
super.viewDidLoad()
let myLayout = CustomLayout()
myLayout.array = self.array
myColView.collectionViewLayout = myLayout
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
cell.myLabel.text = array[indexPath.row]
cell.myLabel.layer.borderColor = UIColor.lightGray.cgColor
cell.myLabel.layer.borderWidth = 1
return cell
}
}
Используемые расширения
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return ceil(boundingBox.height)
}
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return ceil(boundingBox.width)
}
}
Выход:
Ссылка: