Всем привет! Сегодня тема нашего Juniors Talks — UICollectionView.

Что это такое?

UICollectionView – это объект, который хранит в себе совокупность упорядоченных элементов, отображая их с помощью кастомизированных layouts.

Пример:

Чаще всего на практике используют UICollectionView со стандартным UICollectionViewFlowLayout, который отображает элементы в виде обычной сетки и имеет только одно из двух доступных направлений прокрутки (горизонтальная/вертикальная). Но это не всё, что вы можете кастомизировать. Вы можете создать объект UICollectionViewFlowLayout() и посмотреть какие у него есть доступные свойства или почитать здесь.

Считаю, что разговор о UICollectionViewLayout можно вынести в отдельную статью, а сегодня поговорим о начальной инициализации UICollectionView & UICollectionViewCell и разных нюансах реализации.

Итак, начнём…

Для тех кому лень читать ссылка на конечный проект здесь.

Шаг 1

Создаем новый storyboard, добавляем на него viewController, к которому привяжем по всем сторонам collectionView.

Шаг 2

Создаем новый класс для VC, например GalleryViewController, наследуемся от UIViewController

 import UIKit

final class GalleryViewController: UIViewController { 
  override func viewDidLoad() {
  }
} 

Шаг 3

Создадим кастомную ячейку. Для этого нам надо создать Cocoa Touch Class. Нажимаем File → New → File… в открывшимся окне выбираем Cocoa Touch Class, далее класс, от которого будем наследоваться. В нашем случае это UICollectionViewCell. Не забываем выбрать пункт «Also create XIB file»

Для примера я добавил ImageView и UILabel.

О чем не стоит забывать? Об установки reuse Identifier. Для этого нужно выбрать ячейку и в пункте «show the attributes inspector» установить идентификатор.

Вешаем outlets для выше добавленных UI компонентов.
Переходим в класс ячейки и добавляем конфигуратор:

 func configure(by image: UIImage, count: Int) {
  imageView.image = image
  countLabel.text = "\(count)"
} 

Начальные приготовления закончены. Теперь переходим к самому интересному.

Шаг 4

Возвращаемся в наш GalleryViewController. Создаем outlet на collectionView (думаю не нуждается в комментариях)

 @IBOutlet var collectionView: UICollectionView! {
  didSet {
    // 1
    collectionView.delegate = self
    collectionView.dataSource = self
    // 2
    collectionView.register(UINib.init(nibName: "PhotoCell", bundle: nil), forCellWithReuseIdentifier: "photoCell")
    collectionView.backgroundColor = .clear
  }
} 
  1. нужно подписаться на протоколы UICollectionViewDelegate’n’DataSource, чтобы получить доступ к методам.
  2. регистрируем созданный нами выше файл, чтобы использовать его при создании новых ячеек.

Теперь добавим картинки и попробуем их отобразить.

 private var images = [UIImage(named: "image1")!, UIImage(named: "image2")!, UIImage(named: "image3")!, UIImage(named: "image4")!, UIImage(named: "image5")!]

Шаг 5

 extension GalleryViewController: UICollectionViewDelegate, UICollectionViewDataSource {
// 1
  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return images.count
  }
  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  // 2
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! PhotoCell
    let image = images[indexPath.row]
    // 3
    cell.configure(by: image, count: indexPath.row + 1)
    return cell
  }
} 
  1. Метод numberOfItemsInSection обязательный. Он возвращает число элементов, которые будут отображены, в конкретной секции. В нашем случае секция одна;
  2. Метод dequeueReusableCell(withReuseIdentifier: String, for: IndexPath) возвращает ячейку, которую можно переиспользовать;
  3. Теперь когда у нас есть ячейка мы должны ее подготовить для отображения с помощью метода configure, который описали выше.

Запустим и посмотрим что получилось.

Вышло не плохо… Но давайте попробуем сделать так, чтобы на одной строке было всего три ячейки.
Для этого перейдем к заключительному шагу 6 :
Нам нужны будут методы протокола UICVDelegateFlowLayout:

  1. sizeForItemAt — метод задающий определенный размер ячейкам в коллекции;
  2. minimumLineSpacingForSectionAt — метод задающий минимальный отступ между строками, на которых располагаются ячейки;
  3. minimumInteritemSpacingForSectionAt — метод задающий минимальный отступ между ячейками.
 extension GalleryViewController: UICollectionViewDelegateFlowLayout {
  func collectionView(_ collectionView: UICollectionView, 
                      layout collectionViewLayout: UICollectionViewLayout, 
                      sizeForItemAt indexPath: IndexPath) -> CGSize {
                      return CGSize(width: view.frame.width/3, height: view.frame.width/3)
  }
  func collectionView(_ collectionView: UICollectionView, 
                      layout collectionViewLayout: UICollectionViewLayout, 
                      minimumLineSpacingForSectionAt section: Int) -> CGFloat {
                      return 0
  }
  
  func collectionView(_ collectionView: UICollectionView, 
                      layout collectionViewLayout: UICollectionViewLayout, 
                      minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
                      return 0
   }
} 

Вот как выглядит конечный вариант.

Сегодня мы узнали:

  1. как создавать кастомные ячейки для ваших коллекций;
  2. как регистрировать ячейку;
  3. как отобразить ячейки;
  4. как менять размеры ячеек и отступы между ними.

До встречи на следующем Juniors Talks!

Ссылки полезные для прочтения:

  1. https://developer.apple.com/documentation/uikit/uicollectionviewflowlayout;
  2. https://developer.apple.com/documentation/uikit/uicollectionview;
  3. https://www.raywenderlich.com/9334-uicollectionview-tutorial-getting-started;
  4. https://medium.com/@NickBabo/equally-spaced-uicollectionview-cells-6e60ce8d457b.