UICollectionView всегда был мощным инструментом. Многофункциональность и универсальность этого класса трудно не оценить.
Сегодня мы более подробно рассмотрим настройку анимации новых добавленных collection view cell. Давайте начнем!
Начнем мы с создания нового single view app в Xcode.
Перетаскиваем UICollectionViewController на наш холст в Interface Builder, устанавливаем его класс как класс ViewController ( идет как шаблон Xcode), затем выделяем ячейку и в Attribute Inspector устанавливаем ее идентификатор как "ItemCell". Обновим ViewController.swift следующим кодом:
extension UIColor {
static func random() -> UIColor{
return UIColor(
red: CGFloat(drand48()),
green: CGFloat(drand48()),
blue: CGFloat(drand48()),
alpha: 1.0
)
}
}
struct Item {
var color: UIColor
}
class ViewController: UICollectionViewController {
var items = [Item]()
func addItem() { items.append(Item(color: .random())) }
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0...10 { addItem() }
}
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.contentView
.backgroundColor = items[indexPath.item].color
return cell
}
}
Затем мы вставим его в UINavigationController и добавим UIBarButtonItem для того, чтобы можно было добавлять новые элементы.
Через сочетание клавиш Control+Drag в ViewController.swift добавим новый экшен. Связываем его следующим образом:
@IBAction func add(_ sender: UIBarButtonItem) {
addItem()
let indexPath = IndexPath( item: self.items.count - 1, section: 0 )
collectionView?.performBatchUpdates({ self.collectionView?.insertItems(at: [indexPath]) }, completion: nil)
}
Итак у нас получился вполне себе «шаблонный вариант». Только квадратные, случайно окрашенные, постепенно появляющиеся при добавлении ячейки:
Неплохо. Теперь давайте настроим поведение этих ячеек при их добавлении. Для этого нам понадобится собственный пользовательский подкласс UICollectionViewLayout.
В частности, мы будем создавать подкласс UICollectionViewFlowLayout и переопределять несколько функций для настройки поведения вставленных ячеек.
class CustomFlowLayout : UICollectionViewFlowLayout {
var insertingIndexPaths = [IndexPath]()
}
Мы добавили свойство, чтобы отслеживать добавляемые index path во время каждого пакетного обновления collection view.
Чтобы заполнить наше свойство, нам нужно переопределить несколько функций:
override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
super.prepare(forCollectionViewUpdates: updateItems)
insertingIndexPaths.removeAll()
for update in updateItems {
if let indexPath = update.indexPathAfterUpdate, update.updateAction == .insert {
insertingIndexPaths.append(indexPath)
}
}
}
override func finalizeCollectionViewUpdates() {
super.finalizeCollectionViewUpdates()
insertingIndexPaths.removeAll()
}
Отлично. Ничего особенного здесь мы не ждем, просто собираем добавленные (вставленные) index path в начале каждого обновления, а затем очищаем (удаляем) их в конце.
Настоящая магия произойдет тогда, когда мы переопределим последнюю функцию:
override func initialLayoutAttributesForAppearingItem(
at itemIndexPath: IndexPath
) -> UICollectionViewLayoutAttributes? {
let attributes = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
if insertingIndexPaths.contains(itemIndexPath) {
attributes?.alpha = 0.0
attributes?.transform = CGAffineTransform(
scaleX: 0.1,
y: 0.1
)
}
return attributes
}
Здесь мы взяли свойство initialLayoutAttributesForAppearingItem, которое собирался использовать наш collection view для вновь вставленного элемента, и немного изменили его, прежде чем передать обратно в систему.
Мы добавили свойство transform, чтобы уменьшить размер элемента, когда его только добавили. Мы получаем хороший эффект «зума» при добавлении каждого элемента. Теперь добавьте строку в ViewController.swift в начало метода ViewDidLoad:
collectionView?.setCollectionViewLayout(CustomFlowLayout(), animated: false)
Вот что должно получиться:
Это уже выглядит неплохо, но давайте попробуем что-то еще более изящное. Мы можем изменить наш transform на:
attributes?.transform = CGAffineTransform(
translationX: 0,
y: 500.0
)
Одним таким изменением мы можем добиться совершенно иного эффекта:
Класс!
А что дальше?
Конечный проект вы можете скачать тут.
Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!
Урок подготовил: Иван Акулов