Каждый раз при поиске информации касающейся CollectionView я сталкиваюсь с тем, что большая часть основана на использовании сторибордов, но умение работать программно это, как минимум, плюс к карме, поэтому мне хочется поделиться тем как создать вложенный CollectionView, в конце статьи для удобства прикреплю ссылку на гитхаб.
Что должно получиться:
Ну что, приступим, для начала разберемся с основной коллекцией во ViewController
создаем collectionView
добавляем на вью
выставляем констрейнты
class ViewController: UIViewController {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .systemGreen
collection.translatesAutoresizingMaskIntoConstraints = false
return collection
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupConstraints()
}
func setupViews() {
view.addSubview(collectionView)
}
func setupConstraints() {
let safeArea = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 16),
collectionView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor, constant: -16)
])
}
}
Запускаем - видим, что все появилось
Далее, нам нужно создать ячейку-контейнер, в которую мы положим еще один CollectionView, все делаем стандартно
создаем коллекцию
добавляем горизонтальную прокрутку
определяем размеры
добавляем коллекцию в ячейку
выставляем констрейнты
class CollectionContainerViewCell: UICollectionViewCell {
static let id = "CollectionContainerViewCell"
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 170, height: 170)
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .systemYellow
collection.translatesAutoresizingMaskIntoConstraints = false
return collection
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
contentView.addSubview(collectionView)
}
func setupConstraints() {
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
}
Готово, теперь добавляем этот контейнер в нашу основную коллекцию
регистрируем ячейку в collectionView
реализуем протоколы
устанавливаем размеры лэйаута, необходимое количество секций/элементов в секции
class ViewController: UIViewController {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .systemGreen
collection.translatesAutoresizingMaskIntoConstraints = false
collection.register(CollectionContainerViewCell.self, forCellWithReuseIdentifier: CollectionContainerViewCell.id)
collection.delegate = self
collection.dataSource = self
return collection
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupConstraints()
}
func setupViews() {
view.addSubview(collectionView)
}
func setupConstraints() {
let safeArea = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 16),
collectionView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor, constant: -16)
])
}
}
extension ViewController: UICollectionViewDelegate {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionContainerViewCell.id, for: indexPath) as! CollectionContainerViewCell
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.section {
case 0: return CGSize(width: UIScreen.main.bounds.width, height: 200)
case 1: return CGSize(width: UIScreen.main.bounds.width, height: 200)
case 2: return CGSize(width: UIScreen.main.bounds.width, height: 200)
default: return CGSize(width: 0, height: 0)
}
}
}
Смотрим что получилось - все как и хотелось
Продолжаем, нам осталось создать финальную ячейку которую мы положим в ранее созданный контейнер
class InnerCollectionViewCell: UICollectionViewCell {
static let id = "InnerCollectionViewCell"
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
contentView.backgroundColor = .systemRed
}
func setupConstraints() {
}
}
И теперь, наконец-то мы добавляем ее в наш контейнер, все по классике: регистрируем, реализуем протоколы, устанавливаем отступы, кастомизируем в соответствии с требованиями
class CollectionContainerViewCell: UICollectionViewCell {
static let id = "CollectionContainerViewCell"
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 170, height: 170)
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .systemYellow
collection.translatesAutoresizingMaskIntoConstraints = false
collection.register(InnerCollectionViewCell.self, forCellWithReuseIdentifier: InnerCollectionViewCell.id)
collection.delegate = self
collection.dataSource = self
return collection
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
contentView.addSubview(collectionView)
}
func setupConstraints() {
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
}
extension CollectionContainerViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InnerCollectionViewCell.id, for: indexPath) as! InnerCollectionViewCell
return cell
}
}
extension CollectionContainerViewCell: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
}
Смотрим что получилось