3

For the past couple of hours I have tried to accomplish the following:

In a UICollectionview when I highlight (touch down) a cell I want it to bounce inward a few pixels (to a smaller size) and on release bounce back to the original size.

Here is what I have tried so far:

override func collectionView(collectionView: UICollectionView,
didHighlightItemAtIndexPath indexPath: NSIndexPath) {


    collectionView.collectionViewLayout.invalidateLayout()
    let cell = collectionView.cellForItemAtIndexPath(indexPath)
    UIView.transitionWithView(cell!, duration: 1, options: UIViewAnimationOptions.CurveEaseIn, animations: {

        let center = cell?.center
        let frame2 = CGRect(x: 0, y: 0, width: 50, height: 50)
        cell?.frame = frame2
        cell?.center = center!

        }, completion: nil)
}

(I have found much of this code example from this question: UICollectionView: Animate cell size change on selection but this is applied on cell selection. Not highlight. )

So in the didHighlightItemAtIndexPath I set the highlighted cell to a new size. The size is set correct but the following problems arise: 1: it does not animate, 2: it moves to position (0,0) and animate back to center position. It should not move position at all - just the size.

I have seen this effect in the Shazam app for reference. It can be seen when you shazam a song (or view a previously shazamed song) on the buttons below the song name.

Community
  • 1
  • 1
Jell92
  • 183
  • 1
  • 2
  • 6

3 Answers3

8

In the UICollectionViewCell subclass you can use the code below. I would recommend not using touchesBegan and touchesEnded, because overriding these will disable the didSelectItemAt delegate method for the UICollectionView so you will have to take care of that separately through your own delegation. If you are using didSelectItemAt delegate method in your view controller then the correct way to animate is overriding the isHighlighted property.

Swift 3/4-

override var isHighlighted: Bool{
        didSet{
            if isHighlighted{
                UIView.animate(withDuration: 0.2, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 1.0, options: .curveEaseOut, animations: {
                    self.transform = self.transform.scaledBy(x: 0.75, y: 0.75)
                }, completion: nil)
            }else{
                UIView.animate(withDuration: 0.2, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 1.0, options: .curveEaseOut, animations: {
                    self.transform = CGAffineTransform.identity.scaledBy(x: 1.0, y: 1.0)
                }, completion: nil)
            }
        }
    }
Anjan Biswas
  • 7,746
  • 5
  • 47
  • 77
3

Swift 4 little bit refactored code

    override var isHighlighted: Bool {
        didSet { bounce(isHighlighted) }
    }

    func bounce(_ bounce: Bool) {
        UIView.animate(
            withDuration: 0.8,
            delay: 0,
            usingSpringWithDamping: 0.4,
            initialSpringVelocity: 0.8,
            options: [.allowUserInteraction, .beginFromCurrentState],
            animations: { self.transform = bounce ? CGAffineTransform(scaleX: 0.9, y: 0.9) : .identity },
            completion: nil)

    }
UnRewa
  • 2,462
  • 2
  • 29
  • 31
0

Here's the general gist, although it doesn't handle all edge cases.

class MyCollectionViewCell: UICollectionViewCell
{
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        UIView.animateWithDuration(0.5) { () -> Void in
            let shrinkTransform = CGAffineTransformScale(CGAffineTransformIdentity, 0.5, 0.5)
            self.transform = shrinkTransform
        }
    }
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        super.touchesEnded(touches, withEvent: event)
        UIView.animateWithDuration(0.5) { () -> Void in
            self.transform = CGAffineTransformIdentity
        }
    }
}

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource 
{
    let l = UICollectionViewFlowLayout()
    var c: UICollectionView! = nil
    override func viewDidLoad() {
        super.viewDidLoad()
        l.itemSize = CGSize(width: 400, height: 44)
        c = UICollectionView(frame: CGRectZero, collectionViewLayout: l)
        c.delegate = self
        c.dataSource = self
        c.registerClass(MyCollectionViewCell.self, forCellWithReuseIdentifier: "cellID")
        view.addSubview(c)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        c.frame = view.bounds
    }
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
    {
        return 1
    }
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        return 100
    }
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellID", forIndexPath: indexPath) as! MyCollectionViewCell
        if cell.contentView.subviews.count == 0
        {
            let label = UILabel(frame: CGRectZero)
            label.text = "Row: \(indexPath.row)"
            cell.contentView.addSubview(label)
            label.frame = cell.contentView.bounds
            cell.contentView.backgroundColor = UIColor.whiteColor()
        }
        return cell
    }
}

Here's a video: http://cl.ly/1Z3n2S2j0K3t

Sorry for the crappy formatting, Mom called and dinner's ready.

leftspin
  • 2,468
  • 1
  • 25
  • 40
  • What a great answer! Just watched the video and it is precisely what I am after! I will try to implement it and accept the answer if I can get it to work :) Thanks for a great answer - hope dinner was good! – Jell92 Feb 06 '16 at 10:46