Туториал: Автокомпоновка переходов UICollectionViewController'а

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Xcode: 
8.3
Swift: 
3.1

 

Сегодня мы изучим продвинутые возможности UICollectionView и изучим малоизвестную функцию, скрывающуюся внутри UICollectionViewController.

Она позволяет автоматически анимировать переход между различными позиционированиями Collection View при перемещении по стеку navigation controller. Класс!

Этот туториал расчитан на достаточно опытных разработчиков, поэтому некоторые моменты здесь будут опущены, но в конечном проекте вы сможете их подсмотреть.

Начинаем наш проект с SquaresViewController, который выглядит сейчас так:

class SquaresViewController: UICollectionViewController {

  var items = [Item]()

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

  override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath)
    cell.backgroundColor = items[indexPath.item].color
    return cell
  }
}

Ничего особенного, обычный шаблонный код UICollectionViewController. Далее, мы с вами создадим два подкласса. Первый будет для наших «маленьких» ячеек:

class SmallViewController : SquaresViewController {

  init() {
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: 50, height: 50)
    super.init(collectionViewLayout: layout)
    items = (0...50).map { _ in Item(color: .random()) }
  }

Мы заполним его рандомными элементами - маленькими разноцветными ячейками.

Затем, когда пользователь нажмет на одну из этих маленьких ячеек, у нас будет выходить контроллер с большими ячейками. После перехода, мы присвоим свойству .items BigViewController’а наш собственный массив items:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let bigVC = BigViewController()
    bigVC.items = items
    navigationController?.pushViewController(bigVC, animated: true)
  }
}

Опять же, пока ничего необычного. Наконец, прежде чем мы начнем действовать, давайте создадим наш класс BigViewController:

class BigViewController : SquaresViewController {

  init() {
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: 100, height: 100)
    super.init(collectionViewLayout: layout)
  }

Мы увеличим значение свойства layoutSize а затем переопределим collectionView (_: didSelectItemAt :) еще раз.

На этот раз мы просто вызовем popViewController:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    navigationController?.popViewController(animated: true)
  }
}

Если мы запустим приложение сейчас, то все будет работать так, как и ожидалось, но ничего фантастического не произойдет. Просто обычный push нашего navigation controller’а.

Давайте исправим это.

Добавим одну строку кода в функцию инициализации SmallViewController, установив useLayoutToLayoutNavigationTransitions на false:

class SmallViewController : SquaresViewController {
  
init() {
    let layout = UICollectionViewFlowLayout()    
    layout.itemSize = CGSize(width: 50, height: 50)
    super.init(collectionViewLayout: layout)
    useLayoutToLayoutNavigationTransitions = false
    // .. 
  }
  // ..

Тогда мы установим это же свойство на true в BigViewController:

class BigViewController : SquaresViewController {

  init() {
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: 100, height: 100)
    super.init(collectionViewLayout: layout)
    useLayoutToLayoutNavigationTransitions = true
  }
  // ..

Вот и все. UIKit заметит, что мы установили для параметра UseAayoutToLayoutNavigationTransitions свойства BigViewController значение на true и автоматически анимируем переход между двумя лейаутами.

Обратите внимание, что автоматическая анимация перехода, которую предоставляет UIKit, достаточно умна, и выбранная ячейка все еще видна после нашего касания. Просто супер!

И несколько важных замечаний:

Во-первых, в момент когда происходит магия, используется повторно один и тот же UICollectionView. Контроллер, на который осуществляется переход не создает свой собственный collection view. Это может иметь или не иметь значения для вашего приложения, но об этом полезно знать.

Во-вторых, root view controller (SmallViewController в нашем случае) по-прежнему будет установлен как delegate и dataSource, когда будет запущен новый view controller.

Если нам нужно изменить это поведение, мы можем подписаться под протокол UINavigationControllerDelegate и изменять эти значения каждый раз, когда будет показываться другой view controller. Код может выглядеть примерно так:

extension SquaresViewController : UINavigationControllerDelegate {

  func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {

    guard let squaresVC = viewController as? SquaresViewController else { return }
    squaresVC.collectionView?.delegate = squaresVC
    squaresVC.collectionView?.dataSource = squaresVC
  }
}

На сегодня это все.

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Урок подготовил: Иван Акулов

Конечный проект вы можете скачать тут.

Ссылка на источник