I'm currently working on a messaging app and using the compositional layout to create the "New Chat" function of Element/What's App. My problem is, that I want to change the Backgroundcolor of my groups like in What's App. I already managed to change the backgroundcolor of the section, but that is to much.
I put an example from What's App in how it should look.
Could anybody tell me what I have to add, to get this?
Couldn't find anything about group backgroundcolor in the internet What's app example - image my Code:
private func initCollectionView() {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
let section = self.dataSource.snapshot().sectionIdentifiers[sectionIndex]
switch section {
case .localContacts:
return self.listStyleSection(layoutEnvironment: layoutEnvironment)
case .matrixContacts:
return self.listStyleSection(layoutEnvironment: layoutEnvironment)
case .ADContacts:
return self.galleryStyleSection(layoutEnvironment: layoutEnvironment, height: 0.3, width: 0.3, space: 20.0, itemPerLine: 2)
}
}
self.collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
self.view.addSubview(self.collectionView)
self.collectionView.constrain(toBeBelow: self.adButton, padding: .exact(10))
self.collectionView.constrain(toHorizontalBoundsOf: self.view)
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.initDataSource()
self.collectionView.dataSource = self.dataSource
self.collectionView.delegate = self
}```
private func initDataSource() {
dataSource = UICollectionViewDiffableDataSource<ContactSections, ContactEntry>(collectionView: collectionView) {
(collectionView, indexPath, item) -> UICollectionViewCell? in
return self.configureCell(collectionView: collectionView, indexPath: indexPath, item: item)
}
// Registrieren Sie die Zellen für jede Sektion
collectionView.register(ContactCell.self, forCellWithReuseIdentifier: "ContactIdentifier")
collectionView.register(ADCell.self, forCellWithReuseIdentifier: "ADIdentifier")
collectionView.register(ContactCell.self, forCellWithReuseIdentifier: "MXIdentifier")
//swift-format-ignore
collectionView.register(CustomHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
//swift-format-ignore
collectionView.register(CustomFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer")
self.dataSource.supplementaryViewProvider = { [unowned self] collectionView, kind, indexPath in
return self.supplementary(collectionView: collectionView, kind: kind, indexPath: indexPath)
}
}
func supplementary(collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? {
//swift-format-ignore
if kind == UICollectionView.elementKindSectionHeader {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header", for: indexPath) as! CustomHeader
header.titleLabel.text = self.dataSource.snapshot().sectionIdentifiers[indexPath.section].textRepresentation
header.isButtonAllShownActive = false
return header
} else if kind == UICollectionView.elementKindSectionFooter {
//swift-format-ignore
let footer = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer", for: indexPath) as! CustomFooter
return footer
}
return UICollectionReusableView()
}
func configureCell(collectionView: UICollectionView, indexPath: IndexPath, item: ContactEntry) -> UICollectionViewCell {
let section = self.dataSource.snapshot().sectionIdentifiers[indexPath.section]
switch section {
case .localContacts:
return self.configureLocalContactCell(item: item, indexPath: indexPath)
case .matrixContacts:
return self.configureMXContactCell(item: item, indexPath: indexPath)
case .ADContacts:
return self.configureADContactCell(item: item, indexPath: indexPath)
}
}
private func configureMXContactCell(item: ContactEntry, indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MXIdentifier", for: indexPath) as! ContactCell
var contentConfiguration = ContactContentConfiguration()
contentConfiguration.displayName = item.contact!.givenName + " " + item.contact!.familyName
cell.contentConfiguration = contentConfiguration
return cell
}
private func configureLocalContactCell(item: ContactEntry, indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContactIdentifier", for: indexPath) as! ContactCell
var contentConfiguration = ContactContentConfiguration()
contentConfiguration.displayName = item.contact!.givenName + " " + item.contact!.familyName
cell.contentConfiguration = contentConfiguration
return cell
}
private func configureADContactCell(item: ContactEntry, indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ADIdentifier", for: indexPath) as! ADCell
var contentConfiguration = ADContentConfiguration(title: item.adContact!.name!, logo: UIImage(systemName: "person.fill")!)
cell.contentConfiguration = contentConfiguration
return cell
}
func galleryStyleSection(layoutEnvironment: NSCollectionLayoutEnvironment, height: CGFloat = 0.5, width: CGFloat = 0.5, space: CGFloat = 10.0, itemPerLine: Int = 2) -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(width), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize: NSCollectionLayoutSize
groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(height))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: itemPerLine)
group.interItemSpacing = .fixed(space)
let header = self.generateHeader()
let footer = self.generateFooter()
let sec = NSCollectionLayoutSection(group: group)
sec.interGroupSpacing = space
sec.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 20, bottom: 15, trailing: 20)
sec.boundarySupplementaryItems = [header]
return sec
}
func generateHeader() -> NSCollectionLayoutBoundarySupplementaryItem {
let headerSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(40.0))
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
return header
}
func generateFooter(height: CGFloat? = nil) -> NSCollectionLayoutBoundarySupplementaryItem {
let footsize: NSCollectionLayoutSize
if height == nil {
footsize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(30.0))
} else {
footsize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(height!))
}
let footer = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: footsize,
elementKind: UICollectionView.elementKindSectionFooter,
alignment: .bottom)
return footer
}
func listStyleSection(layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
let header = self.generateHeader()
var listStyle = UICollectionLayoutListConfiguration(appearance: .plain)
let sec = NSCollectionLayoutSection.list(using: listStyle, layoutEnvironment: layoutEnvironment)
sec.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
sec.boundarySupplementaryItems = [header]
header.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
return sec
}
There is too much code to read. So what I get is you want list like WhatsApp provided in the background and have a background color behind it. For that purpose, I think your approach is too complicated as I see you are using UICollectionLayoutListConfiguration(appearance: .plain)
and adding contentInsets to that when you can just use other type of appearance. You should simply use List Configuration for all sections and you can create UIcollectionViewListCell
for each section and customise them yourself based on width of the screen and can even have entered images as the cell will adjust size based on that. This is just something I would do to get layout like in the picture shared by you:
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
enum Section: String, CaseIterable {
case section1 = "Section 1"
case section2 = "Section 2"
case section3 = "Section 3"
}
struct SectionItem: Hashable {
let uuid = UUID()
let text: String
}
private var dataSource: UICollectionViewDiffableDataSource<Section, SectionItem>!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
configureCollectionView()
configureDatasource()
generateInitialData(animated: true)
}
// MARK: - Setup CollectionView
private func configureCollectionView() {
collectionView.collectionViewLayout = generateLayout()
collectionView.delegate = self
}
private func generateLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (sectionIndex: Int,
layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
config.headerMode = .supplementary
config.backgroundColor = .purple.withAlphaComponent(0.25)
return .list(using: config, layoutEnvironment: layoutEnvironment)
}
return layout
}
// MARK: - Datasource
private func configureDatasource() {
// list cell
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, SectionItem> { (cell, indexPath, item) in
var contentConfiguration = UIListContentConfiguration.cell()
contentConfiguration.text = item.text
cell.contentConfiguration = contentConfiguration
}
// data source
dataSource = UICollectionViewDiffableDataSource<Section, SectionItem>(collectionView: collectionView) {
(collectionView, indexPath, item) -> UICollectionViewListCell? in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
}
// header r
let headerRegistration = UICollectionView.SupplementaryRegistration
<SimpleHeaderView>(elementKind: UICollectionView.elementKindSectionHeader) {
(supplementaryView, string, indexPath) in
supplementaryView.configure(with: Section.allCases[indexPath.section].rawValue)
}
dataSource.supplementaryViewProvider = { (view, kind, indexPath) in
return self.collectionView.dequeueConfiguredReusableSupplementary(
using: headerRegistration, for: indexPath)
}
}
private func generateInitialData(animated: Bool) {
var snapshot = NSDiffableDataSourceSnapshot<Section, SectionItem>()
let sections = Section.allCases
snapshot.appendSections(sections)
for item in 0...3 {
snapshot.appendItems([SectionItem(text: "\(item)")], toSection: .section1)
}
for item in 0...4 {
snapshot.appendItems([SectionItem(text: "\(item)")], toSection: .section2)
}
for item in 0...2 {
snapshot.appendItems([SectionItem(text: "\(item)")], toSection: .section3)
}
dataSource.apply(snapshot, animatingDifferences: animated)
}
}
You can modify this code to create layout based on your needs. Just make sure to create cell subclass of UIcollectionViewListCell
and not UICollectionViewCell
if you are using the List. You can even modify this Cell Subclass provided by default which is very customisable and add images using contentConfiguration.image
which will add image to left which you can customise or even use different type of list cell configuration like UIListContentConfiguration.valueCell()
based on your needs in this method:
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, SectionItem> { (cell, indexPath, item) in
var contentConfiguration = UIListContentConfiguration.cell()
contentConfiguration.text = item.text
cell.contentConfiguration = contentConfiguration
}