Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Animating Inserts and Deletes on UICollectionView with Swift

DZone's Guide to

Animating Inserts and Deletes on UICollectionView with Swift

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

When users added or deleted filters in the first iteration of my  Filter Chaining application, the updated array of user defined filters was passed into the  FiltersCollectionView instance where is simply invoked  reloadData() on its  UICollectionView. This worked fine, but there was no transition and the new filter suddenly jumped into view.
It would be better to animate the insert or delete and indicate to the user, through movement, how their collection of filters had changed. How to do this wasn’t immediately obvious and this post describes the technique I’ve used.
Inside the view controller, both the  deleteSelectedFilter()  and  addNewFilter() methods update its  userDefinedFilters array:
// deleteSelectedFilter
        userDefinedFilters = userDefinedFilters.filter({!($0 == previousFilter)})

        // addNewFilter
        userDefinedFilters.insert(newFilter, atIndex: userDefinedFilters.count - 1)
and this, in turn, sets the  userDefinedFilters property on the  FiltersCollectionView via a  didSet observer. When that property changes, FiltersCollectionView’s own  didSet observer on  userDefinedFilters looks at the new version of the array to see whether an item has been added (i.e. the length of  oldValue is less that the new value) or deleted (i.e. the length of  oldValue is greater than the new value).
Selecting, adding and deleting items in a  UICollectionView uses  NSIndexPaths rather than, for example, integer indexes or items. Getting the NSIndexPath for a specific item needs an intermediate step. My application always inserts new filters at the penultimate position in the collection view, so I need to find the  NSIndexPath for the last but one cell and invoke  insertItemsAtIndexPath(). To do this, I use on of the extensions that  UICollectionView provides to  NSIndexPath - a new  init() method:
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])

When a user deletes a filter, the application assumes they are deleting the selected filter (a fuller implementation would compare oldValueand userDefinedFilters to figure out what’s been deleted). So, in this case, I need to invoke deleteItemsAtSelectedPaths() for the index paths of the selected items:

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

  uiCollectionView.deleteItemsAtIndexPaths([deleteIndexPath])
As well as adding or removing filters, both of these set  FilterCollectonView’s  selectedFilter property. When a filter is added, the new filter is selected and when a filter is removed, the first filter is selected.
Inside its  didSet observer, I scroll the selected cell into view so that if the user selects a cell which is partially off-screen it becomes fully visible. In this case, I want to find the index of the selected cell inside  userDefinedFilters, then create an  NSIndexPath for that index and finally  selectItemAtIndexPath() on the new  NSIndexPath
To that that, the  didSet observer on  selectedFilter looks like this:
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()
        }
    }
There we have it: rather than cells jarringly appearing and disappearing, a little work with  NSIndexPath and properly using  UICollectionView’s own insert and delete support gives a smooth transition.
All of this code is available at my  GitHub repository here.


Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
java ,mobile ,tutorial ,swift ,ios 8

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}