Collectionview with the Horizontal Scroll with Mulitple Section

collectionview with the horizontal scroll with mulitple section

What you are trying to do is not that difficult. I have created a prototype of what you are looking at. This is how your storyboard's view controller and its document outline look like:

Storyboard

Here's the code for each component

TableViewController

class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func numberOfSections(in tableView: UITableView) -> Int {
return 5
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! MyTableViewCell

return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 160
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return (section%2 == 0) ? "Games we love" : "Apps we love"
}
}

MyTableViewCell

class MyTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

@IBOutlet weak var collectionView: UICollectionView!

let imageNames = ["candy_crush", "cut_the_ropes", "game_1", "little_pet_shop", "zuba"]
let gameNames = ["Candy Crush", "Cut the Ropes", "Arbitrary Game 1", "Littlest Pet Shop", "Zuba"]

override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}

override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)

// Configure the view for the selected state
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageNames.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! MyCollectionViewCell
cell.imageView.image = UIImage.init(named: imageNames[indexPath.row])
cell.titleLabel.text = gameNames[indexPath.row]
cell.detailLabel.text = "Games"

return cell
}
}

MyCollectionViewCell

class MyCollectionViewCell: UICollectionViewCell {

@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var detailLabel: UILabel!
}

This is how it looks like on the simulator

Sample Image

Horizontally scrolling multiple sections with UICollectionViewCompositionalLayout

You can set the primary scroll direction for the collectionViewLayout to horizontal with the following configuration:

let config = UICollectionViewCompositionalLayoutConfiguration()
config.scrollDirection = .horizontal

let sectionProvider = ...(your section provider here)

let cvLayout = UICollectionViewCompositionalLayout(sectionProvider: sectionProvider, configuration: config)

Within the sectionProvider you can create a header pinned to the top of each section as follows:

let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(40))
let headerSupplementary = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .topLeading)
section.boundarySupplementaryItems = [headerSupplementary]

This setup should achieve the scrolling behaviour you desire. Within each section you could use the following group to achieve the same grid layout as the emoji keyboard:

let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 5)

How to make 2 rows in horizontal collection view without using sections

You need to set fix height of the CollectionView and then use sizeForItemAt() under UICollectionViewDelegateFlowLayout which returns CGSize. You have to manage the cell height something like this.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

return CGSize(width: 50.0, height: collectionViewHeight / 2) //<-- Manage height and width accordingly.
}

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.


Related Topics



Leave a reply



Submit