How do I simultaneously update the border of a UICollectionViewCell and perform a batch update of UICollectionView Cells

342 views Asked by At

My goal is to add a shadow around the border of a UICollectionView Cell while simultaneously performing a batch update of the UICollectionView, which will then cause the selected cell to expand in height. The idea is that the shadow around the border will fade into view, or come into view immediately as it does in this GIF, but do so following the actual size of the cell as it grows. As shown in the code, I specifically want the shadow to be around the cell and not within the cell itself - this is to maintain the proper boundaries.

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if weekTasks[indexPath.section].count == 0 {
        let emptyCell = collectionView.dequeueReusableCell(withReuseIdentifier: "emptyCellID", for: indexPath) as! EmptyCell
        return emptyCell
    } else {
        let assignmentCell = collectionView.dequeueReusableCell(withReuseIdentifier: "assignCellID", for: indexPath) as! AssignmentCell
        assignmentCell.myAssignment = weekTasks[indexPath.section][indexPath.item]
        assignmentCell.contentView.backgroundColor = UIColor.white
        return assignmentCell
    }
}

// Sets up size of cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    if weekTasks[indexPath.section].count == 0 {
        return CGSize(width: screenWidth, height: 36 )
    } else if (collectionView.indexPathsForSelectedItems?.contains(indexPath))! {
        return CGSize(width: screenWidth, height: 110)
    } else {
        return CGSize(width: screenWidth, height: 46)
    }
}

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath)
    cell?.contentView.layer.cornerRadius = 12
    cell?.contentView.layer.masksToBounds = true;
    cell?.contentView.backgroundColor = UIColor.white

    cell?.layer.shadowOffset = CGSize(width: 0 ,height: 1)
    cell?.layer.shadowRadius = 4
    cell?.layer.shadowOpacity = 0.2

    collectionView.performBatchUpdates(nil, completion: { done in
        if done {
            print("Sweet")
        }}
    )
}

override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath)
    cell?.contentView.layer.cornerRadius = 0
    cell?.contentView.layer.masksToBounds = true;

    cell?.layer.shadowOpacity = 0
}

My CollectionView Cell

class AssignmentCell: UICollectionViewCell, UIGestureRecognizerDelegate {
var heightConstraint: NSLayoutConstraint?

override init(frame: CGRect) {
    super.init(frame: frame)
    setupViews()
}

let helperView = UIView()

func setupViews() {

    self.contentView.addSubview(helperView)
    let topConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
    let leftConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.left, multiplier: 1, constant: 0)
    let rightConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.right, multiplier: 1, constant: 0)
    let bottomConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
    let heightConstraint = helperView.heightAnchor.constraint(equalToConstant: self.frame.height)
    helperView.addConstraints([topConstraint, leftConstraint, rightConstraint, heightConstraint])

    helperView.addSubview(titleView)
    titleView.frame = CGRect(x: 48, y: (self.frame.height / 2) - 10, width: screenWidth - 190, height: 20)

    helperView.addSubview(checkboxView)
    checkboxView.frame = CGRect(x: 10, y: (self.frame.height / 2) - 9, width: 18, height: 18)
}

func expandCell(to height: CGFloat) {
    heightConstraint?.constant = 110

    UIView.animate(withDuration: 0.3, animations: {
        self.layoutIfNeeded()
    }) { (completed) in
    }
}

}

enter image description here

1

There are 1 answers

4
Deryck Lucian On

It actually follows the cell size. You get that effect because you are setting a fixed cell height and animating the layout change after. You can try to set cell size automatic (only for the expanded one).

In your cell, create a method to animate the size from current value to x (110 in your case).

Add a constraint to be equal to the height of you container and do smth like this:

func expandCell(to height: CGFloat) {
    self.cellHeightConstraint?.constant = height

    UIView.animate(withDuration: 0.3, animations: {
        self.layoutIfNeeded()
    }) { (completed) in
    }
}

// edit

Add a new UIView in your ContentView and in that put all your other views. Set top, bottom, left, right of that view to 0, to its superview(contentView) and a height constraint on that view with @999 priority. See below image:

enter image description here