Статьи

Анимация вставок и удалений в UICollectionView с помощью Swift


Когда пользователи добавили или удалили фильтры в первой итерации моего 
приложения Filter Chaining , обновленный массив пользовательских фильтров был передан в 
 экземпляр
FiltersCollectionView, где просто вызывается 
reloadData ()  в его 
UICollectionView . Это работало нормально, но перехода не было, и новый фильтр внезапно появился.

Было бы лучше анимировать вставку или удалить и указать пользователю, посредством движения, как изменилась их коллекция фильтров.
Как это сделать, было не сразу очевидно, и этот пост описывает технику, которую я использовал.

Внутри контроллера представления 
методы deleteSelectedFilter ()   и 
addNewFilter ()  обновляют свой 
 массив
userDefinedFilters :
// deleteSelectedFilter
        userDefinedFilters = userDefinedFilters.filter({!($0 == previousFilter)})

        // addNewFilter
        userDefinedFilters.insert(newFilter, atIndex: userDefinedFilters.count - 1)

и это, в свою очередь, устанавливает 
userDefinedFilters  свойство на 
FiltersCollectionView  через 
didSet  наблюдателя. При том , что изменения свойств,
FiltersCollectionView собственного «s 
didSet  наблюдатель на 
userDefinedFilters  смотрит на новой версии массива , чтобы увидеть , был ли добавлен элемент (т.е. длина 
OldValue  меньше , что новое значение) или удаляется (т.е. длину 
OldValue  больше, чем новое значение).

Выбор, добавление и удаление элементов в 
UICollectionView  использует 
NSIndexPaths,  а не, например, целочисленные индексы или элементы. Получение
NSIndexPath  для определенного элемента требует промежуточного шага. Мое приложение всегда вставляет новые фильтры в предпоследнюю позицию в представлении коллекции, поэтому мне нужно найти 
NSIndexPath  для последней, кроме одной ячейки, и вызвать 
insertItemsAtIndexPath () . Чтобы сделать это, я использую одно из расширений, которые 
UICollectionView  предоставляет 
NSIndexPath  — новый 
 метод
init () :
init(forItem item: Int, inSection section: Int) -> NSIndexPath

…and the implementation to get the required index path is and insert an item is:

  let insertIndexPath = NSIndexPath(forItem: oldValue.count - 1, inSection: 0)

  uiCollectionView.insertItemsAtIndexPaths([insertIndexPath])

Когда пользователь удаляет фильтр, приложение предполагает, что оно удаляет выбранный фильтр (более полная реализация будет сравнивать  oldValue и  userDefinedFilters,  чтобы выяснить, что было удалено). Итак, в этом случае мне нужно вызвать  deleteItemsAtSelectedPaths ()  для индексных путей выбранных элементов:

let deleteIndexPath = uiCollectionView.indexPathsForSelectedItems()[0] as NSIndexPath

  uiCollectionView.deleteItemsAtIndexPaths([deleteIndexPath])

А также добавление или удаление фильтров, оба эти множества 
FilterCollectonView «s 
selectedFilter  собственности. При добавлении фильтра новый фильтр выбирается, а при удалении фильтра выбирается первый фильтр.

Внутри его 
 обозревателя
didSet я прокручиваю выбранную ячейку, чтобы, если пользователь выбирает ячейку, которая находится за пределами экрана, она становится полностью видимой. В этом случае я хочу найти индекс выбранной ячейки внутри 
userDefinedFilters , затем создать 
NSIndexPath  для этого индекса и, наконец, 
selectItemAtIndexPath ()  в новом 
NSIndexPath

Для этого 
 наблюдатель
didSet на 
selectedFilter  выглядит так:
var selectedFilter : UserDefinedFilter?
    {
        didSet
        {
            var selectedIndex : Int = -1

            for (i: Int, filter: UserDefinedFilter) in enumerate(userDefinedFilters)
            {
                if filter == selectedFilter!
                {
                    selectedIndex = i
                }
            }

            if selectedIndex != -1
            {
                let selectedIndexPath = NSIndexPath(forItem: selectedIndex, inSection: 0)
                uiCollectionView.selectItemAtIndexPath(selectedIndexPath, animated: true, scrollPosition: UICollectionViewScrollPosition.CenteredHorizontally)
            }

            sendActionsForControlEvents(.ValueChanged)
            refresh()
        }
    }

Там у нас это есть: вместо того, чтобы клетки внезапно появлялись и исчезали, небольшая работа с 
NSIndexPath  и правильное использование 
собственной поддержки вставки и удаления
UICollectionView дает плавный переход.

Весь этот код доступен в моем 
репозитории GitHub здесь .