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:
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
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 -
- https://www.zealousweb.com/how-to-use-compositional-layout-in-collection-view/
- https://medium.com/better-programming/ios-13-compositional-layouts-in-collectionview-90a574b410b8
- 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:
- fractionalWidth(): Using fractionalWidth we can set the width/height
of the cell proportional to its parent’s width - fractionalHeight():
Using fractionalHeight, we can set the width/height of the cell
proportional to its parent’s height - absolute(): Using absolute
value, we can set the width/height of the cell to a fixed value - 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:
- 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. - NSCollectionLayoutItem — This is your layout’s cell that renders on
the screen based on the size. - NSCollectionLayoutGroup — It holds the NSCollectionLayoutItem in either horizontal, vertical, or custom forms.
- NSCollectionLayoutSection — This is used to initialize the section by passing along the NSCollectionLayoutGroup. Sections eventually compose the compositional layouts.
Related Topics
What Is Container View in iOS 5 Sdk
Reload Section Without Reloading Section Header
Generating Custom Thumbnail from Alassetrepresentation
Uiscrollview Zooming with Auto Layout
Xctest Build Errors for Test Target Xcode 5:
Use Logical Operator as Combine Closure in Reduce
How to Disable Custom Keyboards (Ios8) for My App
Xcode 8 with Mixed Swift and Objective-C Project Generated "Modulename-Swift.H" Header Not Found
Disable Autolayout Localization Behavior (Rtl - Right to Left Behavior )
Xcode/Swift 'Filename Used Twice' Build Error
How to Get the Current Application Icon in iOS