Reputation: 11
I want to achieve what is in the picture but I'm debating whether to make the collection views inside tableView with different cells (I'm not sure how to do it) or scroll view and insert 2 collection views
link to picture: https://i.sstatic.net/if2pH.png
Upvotes: 0
Views: 693
Reputation: 1892
From iOS 14+, Apple has provide UICollectionViewCompositionalLayout as a type of UICollectionView
layout design for new Modern Collection Views.
This example is provided by Apple on how to use UICollectionViewCompositionalLayout
and UICollectionViewDiffableDataSource
in the new modern way.
For your question, you just need 1 UICollectionView
with UICollectionViewCompositionalLayout
will solved your problem.
I will customize from Apple's example above to make a code preview how to get your desire output.
Step 1: Make TextCell
is UICollectionViewCell
to describe
class TextCell: UICollectionViewCell {
let label = UILabel()
static let reuseIdentifier = "text-cell-reuse-identifier"
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("not implemnted")
}
}
extension TextCell {
func configure() {
label.translatesAutoresizingMaskIntoConstraints = false
label.adjustsFontForContentSizeCategory = true
contentView.addSubview(label)
label.font = UIFont.preferredFont(forTextStyle: .caption1)
let inset = CGFloat(10)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: inset),
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -inset),
label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: inset),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -inset)
])
}
}
Step 2: Make an enum as list of layout you want. In here I make one is horizontal list, 1 is list grid with 3 items and 1 is list grid with 5 items for you to easier understand.
enum SectionLayoutKind: Int, CaseIterable {
case horizontalList, grid5, grid3
var columnCount: Int {
switch self {
case .grid3:
return 3
case .grid5:
return 5
case .horizontalList:
return 1
}
}
}
Step 3: Combine to UICollectionViewCompositionalLayout
and UICollectionViewDiffableDataSource
in your ViewController
.
So the full code is this
class DesiredViewController: UIViewController {
// make an enum of list kind cell as you want to decribe
enum SectionLayoutKind: Int, CaseIterable {
case horizontalList, grid5, grid3
var columnCount: Int {
switch self {
case .grid3:
return 3
case .grid5:
return 5
case .horizontalList:
return 1
}
}
}
var dataSource: UICollectionViewDiffableDataSource<SectionLayoutKind, Int>! = nil
var collectionView: UICollectionView! = nil
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Exptected Output"
configureHierarchy()
configureDataSource()
}
}
extension DesiredViewController {
/// - Tag: PerSection
func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (sectionIndex: Int,
layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
guard let sectionLayoutKind = SectionLayoutKind(rawValue: sectionIndex) else { return nil }
let section: NSCollectionLayoutSection
if sectionLayoutKind == .horizontalList {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.28), heightDimension: .fractionalWidth(0.2))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 10
section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
return section
}
let columns = sectionLayoutKind.columnCount
// The group auto-calculates the actual item width to make
// the requested number of columns fit, so this widthDimension is ignored.
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
let groupHeight = NSCollectionLayoutDimension.fractionalWidth(0.2)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: groupHeight)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns)
section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)
return section
}
return layout
}
}
extension DesiredViewController {
func configureHierarchy() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = .systemBackground
view.addSubview(collectionView)
collectionView.delegate = self
}
func configureDataSource() {
let textCellRegistration = UICollectionView.CellRegistration<TextCell, Int> { (cell, indexPath, identifier) in
// Populate the cell with our item description.
cell.label.text = "\(identifier)"
cell.contentView.backgroundColor = .cornflowerBlue
cell.contentView.layer.borderColor = UIColor.black.cgColor
cell.contentView.layer.borderWidth = 1
cell.contentView.layer.cornerRadius = SectionLayoutKind(rawValue: indexPath.section)! == .grid5 ? 8 : 0
cell.label.textAlignment = .center
cell.label.font = UIFont.preferredFont(forTextStyle: .title1)
}
dataSource = UICollectionViewDiffableDataSource<SectionLayoutKind, Int>(collectionView: collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
// Return the cell of your choose
// in here I use just 1 cell is text cell but you can choose as any custom cell you design for each section
return SectionLayoutKind(rawValue: indexPath.section)! == .horizontalList ?
collectionView.dequeueConfiguredReusableCell(using: textCellRegistration, for: indexPath, item: identifier) :
collectionView.dequeueConfiguredReusableCell(using: textCellRegistration, for: indexPath, item: identifier)
}
// initial data
let itemsPerSection = 10
var snapshot = NSDiffableDataSourceSnapshot<SectionLayoutKind, Int>()
SectionLayoutKind.allCases.forEach {
snapshot.appendSections([$0])
let itemOffset = $0.rawValue * itemsPerSection
let itemUpperbound = itemOffset + itemsPerSection
snapshot.appendItems(Array(itemOffset..<itemUpperbound))
}
dataSource.apply(snapshot, animatingDifferences: false)
}
}
extension DesiredViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
}
}
And this is the result
Upvotes: 1