Beginning Collection Views - Part 18: Section 2: | Ray Wenderlich

In this challenge you'll animate the deletion of cells on your own and get a peek at view animations, while you're here.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/4448-beginning-collection-views/lessons/18

I have a problem. When i tapping add item before editing/deleting for the first time i have following problem:Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value ||| In that part of my code:

  override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attributes = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath),
        deletedItems!.contains(itemIndexPath) else {
            return nil
    }
    attributes.alpha = 0.5
    attributes.transform = CGAffineTransform(scaleX: 0.15, y: 0.15)
    attributes.zIndex = 5
    return attributes
}

deletedItems!.contains(itemIndexPath) else

I just cant comprehend why this method called. Any suggestion?
My MainViewController:

import UIKit

class MainViewController: UICollectionViewController {
	@IBOutlet private weak var addButton:UIBarButtonItem!
	@IBOutlet private weak var deleteButton:UIBarButtonItem!

private let dataSource = DataSource()

override func viewDidLoad() {
	super.viewDidLoad()
	// Set up a 3-column Collection View
	let width = view.frame.size.width / 3
	let layout = collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
	layout.itemSize = CGSize(width:width, height:width)
    layout.sectionHeadersPinToVisibleBounds = true
	// Refresh control
	let refresh = UIRefreshControl()
	refresh.addTarget(self, action: #selector(self.refresh), for: UIControlEvents.valueChanged)
	collectionView?.refreshControl = refresh
	// Toolbar
	navigationController?.isToolbarHidden = true
	// Edit
	navigationItem.leftBarButtonItem = editButtonItem
}

override func didReceiveMemoryWarning() {
	super.didReceiveMemoryWarning()
	// Dispose of any resources that can be recreated.
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
	if segue.identifier == "DetailSegue" {
		if let dest = segue.destination as? DetailViewController {
			dest.park = sender as? Park
		}
	}
}

override func setEditing(_ editing: Bool, animated: Bool) {
	super.setEditing(editing, animated: animated)
	addButton.isEnabled = !editing
	collectionView?.allowsMultipleSelection = editing
	if !editing {
		navigationController?.isToolbarHidden = true
	}
	guard let indexes = collectionView?.indexPathsForVisibleItems else {
		return
	}
	for index in indexes {
		let cell = collectionView?.cellForItem(at: index) as! CollectionViewCell
		cell.isEditing = editing
	}
}

@IBAction func addItem() {
	let index = dataSource.indexPathForNewRandomPark()
    let layout = collectionView?.collectionViewLayout as! FlowLayout
    layout.addedItem = index
		collectionView?.insertItems(at: [index])
	}

@objc func refresh() {
	addItem()
	collectionView?.refreshControl?.endRefreshing()
}

@IBAction func deleteSelected() {
	if let selected = collectionView?.indexPathsForSelectedItems {
        print(selected)
		dataSource.deleteItemsAtIndexPaths(selected)
        let layout = self.collectionView?.collectionViewLayout as! FlowLayout
        layout.deletedItems = selected
        selected.forEach({self.collectionView?.cellForItem(at: $0)?.isSelected = false})
		collectionView?.deleteItems(at: selected)
		navigationController?.isToolbarHidden = true
	}
}

}

extension MainViewController {
    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind:String, at indexPath: IndexPath) -> UICollectionReusableView {
        let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "SectionHeader", for: indexPath) as! SectionHeader
        let mySection = Section()
        mySection.title = dataSource.titleForSectionAtIndexPath(indexPath)
        mySection.count = dataSource.numberOfParksInSection(indexPath.section)
        view.section = mySection
        return view
    }

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    return dataSource.numberOfSections
}

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

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
	let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
    cell.park = dataSource.parkForItemAtIndexPath(indexPath)
    cell.isEditing = isEditing
    cell.isSelected = false
	return cell
}

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
	if !isEditing {
		let park = dataSource.parkForItemAtIndexPath(indexPath)
		performSegue(withIdentifier: "DetailSegue", sender: park)
	} else {
        let cell = self.collectionView?.cellForItem(at: indexPath) as! CollectionViewCell
        cell.isSelected = !cell.isSelected
		navigationController?.isToolbarHidden = false
	}
}

override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
	if isEditing {
		if let selected = collectionView.indexPathsForSelectedItems, selected.count == 0 {
			navigationController?.isToolbarHidden = true
		}
	}
}

}

My flowLayout:

import UIKit

class FlowLayout: UICollectionViewFlowLayout {
    var addedItem: IndexPath?
    var deletedItems: [IndexPath]?

//add border to layout
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var result = [UICollectionViewLayoutAttributes]()
    if let attributes = super.layoutAttributesForElements(in: rect){
        for item in attributes {
            let cellAttributes = item.copy() as! UICollectionViewLayoutAttributes
            if item.representedElementKind == nil {
                let frame = cellAttributes.frame
                cellAttributes.frame = frame.insetBy(dx: 2.0, dy: 2.0)
            }
            result.append(cellAttributes)
        }
    }
    return result
}


override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attributes = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath),
    let added = addedItem,
        added == itemIndexPath else {
            return nil
    }
    //set new attributes
    attributes.center = CGPoint(x: collectionView!.frame.width - 23.5, y: -24.5)
    attributes.alpha = 1.0
    attributes.transform = CGAffineTransform(scaleX: 0.15, y: 0.15)
    attributes.zIndex = 5
    return attributes
}

override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attributes = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath),
        deletedItems!.contains(itemIndexPath) else {
            return nil
    }
    attributes.alpha = 0.5
    attributes.transform = CGAffineTransform(scaleX: 0.15, y: 0.15)
    attributes.zIndex = 5
    return attributes
}

}

Make it work after adding following:

override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attributes = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath),
        let deletedItems = deletedItems,
        deletedItems.contains(itemIndexPath) else {
            return nil
    }
    attributes.alpha = 0.5
    attributes.transform = CGAffineTransform(scaleX: 0.15, y: 0.15)
    attributes.zIndex = 5
    return attributes
}

let deletedItems = deletedItems,

Still don’t understand why i need to check if deletedItems is nill or not…

Hi there! In this case, there may be no deleted items in that array.

This course was updated this past fall for iOS 12/Xcode 10, and I know there were a few bugs in the iOS 11 version which you may be running up against.

I highly recommend you move forward with the updated version: https://www.raywenderlich.com/6308-beginning-collection-views/lessons/1