'Compositional layout with centred items and centred paging
Need some help here. I'm building an interesting layout, where cells are centered depending on their count. Probably, best thing to explain is to illustrate a little demo. Here it is:
On the left we have a behaviour of how layout acts depending on items in datasource. On the right - illustration of seven cells in collection view.
I have some thoughts about triple grouping, but probably you can help me with more simple solution using compositional layout.
Thanks!
Solution 1:[1]
Okay, so, I figured out how to make such kind of layout.
Firstly, we need to deal with groups. Here is basic illustration what we need to do:
Probably, at this point you'll have questions, that nice. I can't answer all of them, but here is algorithm:
- We need to separate all our items into chunks by 2. So each group must have max 2 items. And max groups in section must be equal to 2
- If item if group is less than 2, we apply left inset to group contentInsets.
- Section can have orthogonalScrollingBehavior = .groupPagingCentered if needed.
Probably in code it can be achieved in this way:
private func makeLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { [weak self] _, _ -> NSCollectionLayoutSection? in
guard let self = self else { return nil }
var nestedGroupHeight: CGFloat = 250
if numberOfItems > 2 {
nestedGroupHeight = 500
}
let chunkSize = 4
let chunks = stride(from: 0, to: numberOfItems, by: chunkSize).map {
Array($0..<min($0 + chunkSize, numberOfItems)).count
}
var allGroups: [NSCollectionLayoutGroup] = []
for chunk in chunks {
var group: [NSCollectionLayoutGroup] = []
// This code probably can be reduced to 2-3 lines.
// For now - it's for better understanding on what's going on here
if chunk == 4 { // 4 speakers, two groups of 2
group.append(self.createGroup(2))
group.append(self.createGroup(2))
} else if chunk == 3 { // 3 items, one group of 2 and one group of 1
group.append(self.getTopSpeakersGroup(2))
group.append(self.getTopSpeakersGroup(1))
} else if chunk == 2 { // 2 items, one group of 2
group.append(self.getTopSpeakersGroup(2))
} else if chunk == 1 { // 1 items, one group of 1
group.append(self.getTopSpeakersGroup(1))
}
let mainGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(
widthDimension: .absolute(UIScreen.main.bounds.width - 45),
heightDimension: .estimated(nestedGroupHeight)),
subitems: group
)
group.interItemSpacing = .fixed(10)
allGroups.append(group)
}
let nestedGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .absolute(UIScreen.main.bounds.width - 45),
heightDimension: .estimated(nestedGroupHeight)),
subitems: allGroups)
nestedGroup.interItemSpacing = .fixed(15)
let layoutSection = NSCollectionLayoutSection(group: nestedGroup)
layoutSection.orthogonalScrollingBehavior = .groupPagingCentered
return layoutSection
}
}
private func createGroup(_ count: Int) -> NSCollectionLayoutGroup {
let itemWidth = BoardTopMemberCell.width
let groupHeight: CGFloat = 250
let groupWidth = UIScreen.main.bounds.width - 45
let trailingItem = NSCollectionLayoutItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .absolute(itemWidth),
heightDimension: .estimated(groupHeight)
)
)
var leftInset: CGFloat = groupWidth / 2 - 15/2 - itemWidth
if count < 2 {
leftInset = groupWidth / 2 - itemWidth / 2
}
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth),
heightDimension: .estimated(groupHeight))
let trailingGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [trailingItem]
)
trailingGroup.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: leftInset, bottom: 0, trailing: 0)
trailingGroup.interItemSpacing = .fixed(15)
return trailingGroup
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Pete Streem |