48

In UICollectionView, I want to give the whole section a uniform background color, instead of for a single cell or for the whole collection view.

I don't see any delegate method to do that, any suggestions?

lichen19853
  • 1,410
  • 2
  • 14
  • 21

10 Answers10

14

First, make a UICollectionReusableView to be your background view. I've simply set mine to be red.

class SectionBackgroundDecorationView: UICollectionReusableView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = .red
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Then, in YourCollectionViewController:

static let background = "background-element-kind"

init() {
    super.init(collectionViewLayout: YourCollectionViewController.createLayout())
}

static func createLayout() -> UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { (sectionIndex: Int,
        layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        
        // Use sectionIndex to control the layout in each section
        if sectionIndex == 0 {
            
            // Customise itemSize, item, groupSize, group to be whatever you want
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                                  heightDimension: .fractionalHeight(1))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
       
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                                   heightDimension: .absolute(170))
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

            let section = NSCollectionLayoutSection(group: group)
            
            // Create a sectionBackground
            let sectionBackground = NSCollectionLayoutDecorationItem.background(
                    elementKind: background)

            section.decorationItems = [sectionBackground]
            return section
            
        } else {
            
          // Your layout for other sections, etc
          // You don't have to set a background for other sections if you don't want to
        }
    }
    layout.register(SectionBackgroundDecorationView.self, forDecorationViewOfKind: background)
    return layout
}

These links to documentation helped me:

Henry Noon
  • 334
  • 3
  • 7
  • 2
    Is there a way to give different background colours to different sections based on the sectionData ? – Faizyy Apr 12 '22 at 11:49
12

The new UICollectionViewCompositionalLayout introduced in iOS 13 have a property named decorationItems for adding decoration items conveniently, which you could use to add a background for the section.

let section = NSCollectionLayoutSection(group: group)
section.decorationItems = [
   NSCollectionLayoutDecorationItem.background(elementKind:"your identifier")
]
leavez
  • 2,119
  • 2
  • 27
  • 36
4

The idea is to override UICollectionViewLayoutAttributes to add a color attribute. And then override UICollectionReusableView apply the color to the view background.

https://github.com/strawberrycode/SCSectionBackground

dangthaison.91
  • 269
  • 1
  • 5
  • When i make this example to support all orientation, while moving from portratoit to landscappre or vice versa the app crashes with below error, – EXC_BAD_ACCESS Dec 27 '19 at 10:20
  • SCSectionBackground[34266:300939] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path ( {length = 2, path = 0 - 0}) changed from index path: ( {length = 2, path = 0 - 0}); element kind: (sectionBackground); frame = (0 0; 375 116); zIndex = -1; – EXC_BAD_ACCESS Dec 27 '19 at 10:21
  • without invalidating the layout'. Could you help to resolve this ? – EXC_BAD_ACCESS Dec 27 '19 at 10:21
2

I haven't tried this out yet, but it looks to me that you need to use decoration views if you want a background behind your cells (like the shelf in the Books app). I think you should be able to have different views for each section, and set them up using the delegate method layoutAttributesForDecorationViewOfKind:atIndexPath:.

iOS Dev
  • 4,143
  • 5
  • 30
  • 58
rdelmar
  • 103,982
  • 12
  • 207
  • 218
2

refer to:
strawberrycode: Use DecorationView as background
devxoul: Use SupplementaryElement as background
airbnb: Use SupplementaryElement as background

I need to compatible with IGListKit, so I use decorationView as background. The layout is subclass from UICollectionViewFlowLayout for common use case. My implementation: different section background

Jules
  • 631
  • 6
  • 14
0

In collection view every section can have a supplementary views, so put supplementary views for each section then set background color to supplementary views instead of section cells. I hope it will help.

Jirune
  • 2,310
  • 3
  • 21
  • 19
  • When reading about supplementary views, I only find examples when it is used for headers & footers. Do you have any example and/or working code for your answer. – Johan Karlsson Mar 17 '14 at 12:44
  • I have done it by using collection view custom layout. In custom layout my supplementary view frame is equal to that entire section frame. – Jirune Mar 19 '14 at 07:36
  • 2
    How did you manage to do that? if you could tell that would be great. Thanks – Shwet Solanki May 09 '14 at 15:23
0

One of the classic approach is to create a Custom Supplementary Kind and provide your custom view in CollectionView Section background. It will give you the ability to customize section backgrounds. Refer to https://stackoverflow.com/a/63598373/14162081

-1

I went off of this repo here https://github.com/SebastienMichoy/CollectionViewsDemo/tree/master/CollectionViewsDemo/Sources/Collections%20Views

Swift 3

subclass uicollectionreusableview

class SectionView: UICollectionReusableView {
   static let kind = "sectionView"
}

subclass uicollectionViewFlowLayout

class CustomFlowLayout: UICollectionViewFlowLayout {

// MARK: Properties

var decorationAttributes: [IndexPath: UICollectionViewLayoutAttributes]
var sectionsWidthOrHeight: [IndexPath: CGFloat]


// MARK: Initialization

override init() {
    self.decorationAttributes = [:]
    self.sectionsWidthOrHeight = [:]

    super.init()
}

required init?(coder aDecoder: NSCoder) {
    self.decorationAttributes = [:]
    self.sectionsWidthOrHeight = [:]

    super.init(coder: aDecoder)
}

// MARK: Providing Layout Attributes

override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return self.decorationAttributes[indexPath]
}

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var attributes = super.layoutAttributesForElements(in: rect)
    let numberOfSections = self.collectionView!.numberOfSections
    var xOrYOffset = 0 as CGFloat

    for sectionNumber in 0 ..< numberOfSections {
        let indexPath = IndexPath(row: 0, section: sectionNumber)
        let numberOfItems = self.collectionView?.numberOfItems(inSection: sectionNumber)
        let sectionWidthOrHeight = numberOfItems == 0 ? UIScreen.main.bounds.height : collectionViewContentSize.height//self.sectionsWidthOrHeight[indexPath]!
        let decorationAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: SectionView.kind, with: indexPath)
        decorationAttribute.zIndex = -1

        if self.scrollDirection == .vertical {
            decorationAttribute.frame = CGRect(x: 0, y: xOrYOffset, width: self.collectionViewContentSize.width, height: sectionWidthOrHeight)
        } else {
            decorationAttribute.frame = CGRect(x: xOrYOffset, y: 0, width: sectionWidthOrHeight, height: self.collectionViewContentSize.height)
        }

        xOrYOffset += sectionWidthOrHeight

        attributes?.append(decorationAttribute)
        self.decorationAttributes[indexPath] = decorationAttribute
    }

    return attributes
}
}

implement this

CollectionView delegate function

func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) {

    Log.printLog(identifier: elementKind, message: indexPath)
    if elementKind == UICollectionElementKindSectionHeader, let view = view as? ProfileViewHeaderView {

        view.backgroundColor = UIColor(red: (102 / 255.0), green: (169 / 255.0), blue: (251 / 255.0), alpha: 1)
    } else if elementKind == SectionView.kind {
        let evenSectionColor = UIColor.black
        let oddSectionColor = UIColor.red

        view.backgroundColor = (indexPath.section % 2 == 0) ? evenSectionColor : oddSectionColor
    }
}

This is important

let layout = CustomFlowLayout()
layout.register(SectionView.self, forDecorationViewOfKind: SectionView.kind)

register the UICollectionReusableView with layout not collectionView.

one more thing. I messed around with the height in layoutAttributesForElements. you should change it for your own project.

Woody Jean-louis
  • 475
  • 6
  • 12
-1

Its very simple just use this default UICollectionViewDelegate's method, it will works

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    print(indexPath.item)

    let evenSectionColor = UIColor.clear
    let oddSectionColor = UIColor.white
    cell.contentView.backgroundColor = (indexPath.item % 2 == 0) ? evenSectionColor : oddSectionColor

}
Harie venad
  • 79
  • 1
  • 3
-4

I have changed the background color of each section in a very simple manner in the following method: But I was not sure whether it is the right thing to do. But it did work.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    FamilyCalendarCellItemCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"calendarItem" forIndexPath:indexPath];
    Event *event;
    _headerView = [collectionView dequeueReusableSupplementaryViewOfKind:
                   UICollectionElementKindSectionHeader withReuseIdentifier:@"EventHeader" forIndexPath:indexPath]; //headerView is declared as property of Collection Reusable View class
    if(indexPath.section==0) {

        cell.backgroundColor=[UIColor orangeColor];
        }
    else if(indexPath.section==1) {

        cell.backgroundColor=[UIColor yellowColor];

    }

    return cell;
}
Taylan Aydinli
  • 4,333
  • 15
  • 39
  • 33
  • This only changes the background colour of the cells. The question is asking for how to change the background colour of the collection view itself within a given section – TylerJames Nov 24 '16 at 16:59