Uicollectionview Multiple Sections and Headers

UICollectionView with two headers

You just need to use a simple trick.Show header and footer both for all sections.

In which section you do not want to show footer just pass its size zero as :--

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
if(section==0)
{
return CGSizeZero;
}

return CGSizeMake(320, 50);
}

Here I have used two sections like

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 2;
}

And passed no of rows in only one sections that is the last one as

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if (section==1) {
return 20;
}
return 0;
}

And here is my output ---

Sample Image

Red View is header and Green One is footer.

Here u can get the entire Implementation File

How to setup a 1 header with multiple sections using UICollectionViewController

Here’s a simple implementation, that only overrides an optional method on the UICollectionViewDataSource protocol:

func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let section = indexPath.section

switch section {
case 0:
let TitleHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: homeHeaderReuseIdentifier, for: indexPath) as! TitleHeader
return TitleHeader
default:
let Section1Header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionSpacingHeaderReuseIdentifier, for: indexPath) as! Section1Header
return Section1Header
}
case UICollectionElementKindSectionFooter:
let FooterView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: homeFooterReuseIdentifier, for: indexPath) as! FooterView
return FooterView
default:
return UICollectionReusableView()
}
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100.0)
}

UICollectionView with multiple sections

In these kind of scenarios, what you must be using is iOS 13's Compositional Layouts in CollectionView. Here are 2 tutorials for you to understand it -

  1. https://www.zealousweb.com/how-to-use-compositional-layout-in-collection-view/
  2. https://medium.com/better-programming/ios-13-compositional-layouts-in-collectionview-90a574b410b8
  3. WWDC video - https://developer.apple.com/videos/play/wwdc2019/215/

For your case, I've tweaked the example in the second tutorial

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (section == 0) ? 10 : 9
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath)
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimeSelectorCell.cellIdentifier, for: indexPath) as! TimeSelectorCell

return cell
}

private func commonInit() {
let cv = UICollectionView(frame: view.bounds, collectionViewLayout: createLayoutDiffSection())
cv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
TimeSelectorCell.register(in: cv)

cv.dataSource = self
cv.delegate = self

addSubview(cv)
}

Till now, it is almost same as your code. Now the createLayoutDiffSection() method is -

func createLayoutDiffSection() -> UICollectionViewLayout {

let layout = UICollectionViewCompositionalLayout { (sectionIndex: Int,
layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in

let seperateFlow = true

let columns = (sectionIndex == 0) ? 10 : 9

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 2, bottom: 0, trailing: 2)

let groupHeight = NSCollectionLayoutDimension.fractionalWidth(0.2)

let widthFraction:CGFloat = (sectionIndex == 0) ? 1 : 0.9

let groupSizeWidth:CGFloat = seperateFlow ? (widthFraction * 2) : 1

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(groupSizeWidth),
heightDimension: groupHeight)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns)

let section = NSCollectionLayoutSection(group: group)
if seperateFlow {
section.orthogonalScrollingBehavior = .continuous
}
section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0)
return section
}
return layout
}

If the scroll is not separate for both the sections, then set -

let seperateFlow = false

Important points here to understand are -

Size Classes

There are four size options that we can use according to our needs, and that will eliminate the need for the calculation to achieve the desired output. Let’s see all the size classes:

  1. fractionalWidth(): Using fractionalWidth we can set the width/height
    of the cell proportional to its parent’s width
  2. fractionalHeight():
    Using fractionalHeight, we can set the width/height of the cell
    proportional to its parent’s height
  3. absolute(): Using absolute
    value, we can set the width/height of the cell to a fixed value
  4. estimate(): Using estimate value, we can set the cell’s width/height
    according to the content size. The system will decide the optimal
    width/height for the content.

Core Concepts

To build any compositional layout, the following four classes need to be implemented:

  1. NSCollectionLayoutSize — The width and height dimensions are of the
    type NSCollectionLayoutDimension which can be defined by setting the
    fractional width/height of the layout (percentage relative to its
    container), or by setting the absolute or estimated sizes.
  2. NSCollectionLayoutItem — This is your layout’s cell that renders on
    the screen based on the size.
  3. NSCollectionLayoutGroup — It holds the NSCollectionLayoutItem in either horizontal, vertical, or custom forms.
  4. NSCollectionLayoutSection — This is used to initialize the section by passing along the NSCollectionLayoutGroup. Sections eventually compose the compositional layouts.

How to create multiple Sections in UICollectionView with multiple Headers using RxDatasource

RxCollectionViewSectionedReloadDataSource<SectionModel> expects that you will bind items of SectionModel type, because you passed SectionModel as a generic parameter. Apparently, you would like to use StudentModel. To achieve this, you might make your StudentModel conform to SectionModelType protocol, and then use RxCollectionViewSectionedReloadDataSource<StudentModel>:

extension StudentModel: SectionModelType {
// implement
}

let dataSource = RxCollectionViewSectionedReloadDataSource<StudentModel>(configureCell: { (datasource, collectionView, indexPath, element) in
// configure a cell
})
studentCells.bind(to: studentsView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag) // don't forget to setup disposal

But I suppose that StudentModel is describing a single cell, rather than an entire section. Is such case, it may be a better idea to map StudentModel to SectionModel, like this:

let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel>(configureCell: { (datasource, collectionView, indexPath, element) in
// configure a cell
})
studentCells
.map { [SectionModel(model: "", items: $0)] }
.bind(to: studentsView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)

Obviously, I mapped all your studentCells into a single section, which might not be your case. In more complex scenarios, you might consider implementing a custom type conforming to SectionModelType.

Also, you may pass something more valuable than an empty string as a model, but again it depends on your needs.

Attension!
In an example above SectionModel stands for RxDataSources.SectionModel, not:

enum SectionModel {
case SectionOne(items: [SectionItem])
case SectionTwo(items: [SectionItem])
}

Multiple Section in UICollectionView

Make sure assigning DataSource & Delegate to CollectionView

1.Give number of sections you wanna show using below method

func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}

2.Set item count of each sections for CollectionView using below method.

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (section == 0) ? list.count : list2.count
}

3.Assigning cell for each item to CollectionView.

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

//If you are using multiple cells based on section make condition

if indexPath.section == 0 {
//make sure the identifier of your cell for first section
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath)
// do your stuffs
return cell
}else{
//make sure the identifier of your cell for second section
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell2", for: indexPath)
// do your stuffs
return cell
}

}

how to implement a uicollectionview that has multiple sections and subsections?

I've implemented this using UICollectionViewCompositionalLayout. Used the
UICollectionViewCompositionalLayout { [self] (sectionNumber, environment) -> NSCollectionLayoutSection? in .... } closure to setup the layout items and group them vertically and horizontally to form the layout for the entire section.

For reference -

To create an individual item (H: 50, W: 50) -

let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .absolute(50), heightDimension: .absolute(50)))

To create groups (Horizontal) -

let group = NSCollectionLayoutGroup.horizontal(layoutSize:
NSCollectionLayoutSize(widthDimension:
.absolute(item.layoutSize.widthDimension.dimension * CGFloat((data.count))),
heightDimension: .estimated(50)), subitem: item, count: (data.count))

Group - vertical

 let subGroup = NSCollectionLayoutGroup.vertical(layoutSize:
NSCollectionLayoutSize(widthDimension:
.absolute(CGFloat(width)),
heightDimension:
.absolute(heightForIndividualItem * CGFloat(data.count))),
subitems: group)

Collection View Multiple horizontal Section Header

This is working fine(here we are creating labels and buttons in collection view header according to the figure) :-

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderView" forIndexPath:indexPath];

CGFloat x=0,y=0;

for (int i = 0;i<[_officelist count];i++)
{
id val=[officeSize objectAtIndex:i];
CGFloat val1=[val floatValue];
UILabel *newLabel = [[UILabel alloc] initWithFrame:CGRectMake(x, 10,val1-1,35)];

newLabel.text=[NSString stringWithFormat:@"%@",_officelist[i]];

newLabel.textAlignment = NSTextAlignmentCenter;

newLabel.backgroundColor = [UIColor greenColor];
[self.roadmapCollectionView addSubview:newLabel];

x=x+val1+1;
}
for (int i=0; i<_departmentlist.count; i++) {

dept=[_departmentlist objectAtIndex:i];
id val=[officeSize objectAtIndex:i];
CGFloat val1=[val floatValue];

float val2=val1/[dept count];
//NSLog(@"DEPT SIZE - %f",val2);

for (int j = 0; j < [dept count]; j++)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(y, 50,val2-1, 25);
[button setBackgroundColor:[UIColor yellowColor]];
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[button setTitle:[NSString stringWithFormat:@"%@",dept[j]] forState:UIControlStateNormal];

[self.roadmapCollectionView addSubview:button];

[deptSize addObject:[NSNumber numberWithFloat:y]];

y=y+val2+1;

}
}

return reusableView;
}

Having one Main sticky Header with Multiple Sections of UICollection views? Xcode - Swift

You can use collection view's collectionView:viewForSupplementaryElementOfKind:atIndexPath: and if it is flow layout use also referenceSizeForHeaderInSection



Related Topics



Leave a reply



Submit