0

What I expect: That when I press down on the button it shrinks to 75% and stays at its 75% shrunken size until I let go.

What is happening: The buttons shrinks to 75%, but as soon as it finishes the duration of the animation, while my finger is still pressed on the button, it grows back to its original value.

I am called the following when a button is "Touched Down".

import Foundation
import UIKit

extension UIButton {

func shrink() {

    let shrink = CABasicAnimation(keyPath: "transform.scale")
    shrink.fromValue = 1.0
    shrink.toValue = 0.75
    shrink.duration = 0.5
    shrink.isRemovedOnCompletion = false
    layer.add(shrink, forKey: nil)

   }
 }
mpc75
  • 937
  • 9
  • 22
  • Does this answer your question? [CABasicAnimation resets to initial value after animation completes](https://stackoverflow.com/questions/6059054/cabasicanimation-resets-to-initial-value-after-animation-completes) – Tim Apr 14 '20 at 13:10
  • @Tim thanks for the suggestion but it does not. I already had ```shrink.isRemovedOnCompletion = false``` which didn't solve the problem. – mpc75 Apr 14 '20 at 13:16
  • call this function on touchupinside – Arun Apr 14 '20 at 13:35
  • @mpc75 You need to update the underlying value or also use `fillMode = .forwards`. My answer below explains it better – Tim Apr 14 '20 at 13:37

1 Answers1

1

The size resets because CABasicAnimation doesn't update the layer's underlying value, only the presentation layer. So when the animation ends, it goes back to using the underlying value.

The best way to deal with this is to set the transform scale before the animation starts so that it is the correct value at the end.

func shrink() {
    transform = CGAffineTransform(scaleX: 0.75, y: 0.75) // Set final state

    let shrink = CABasicAnimation(keyPath: "transform.scale")
    shrink.fromValue = 1.0
    shrink.toValue = 0.75
    shrink.duration = 0.5
    layer.add(shrink, forKey: nil)

}

You can also set the fillMode to .forwards and isRemovedOnCompletion to false, which will keep the presentation layer changes in place, but this doesn't update the layer's actual scale, so the button will keep the original scale for tap detection. (You need to set the transform's scale as above to correct it.)

func shrink() {
    let shrink = CABasicAnimation(keyPath: "transform.scale")
    shrink.fromValue = 1.0
    shrink.toValue = 0.75
    shrink.duration = 0.5
    shrink.isRemovedOnCompletion = false
    shrink.fillMode = .forwards
    layer.add(shrink, forKey: nil)
}

You can also use a view animation which will have the same visual effect, but also update the scale:

UIView.animate(withDuration: 0.5) { [weak self] in
    self?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
}
Tim
  • 110
  • 1
  • 10
  • Thanks, this worked and is precisely what I asked for. I also need it to grow back to original size if it's touched up outside which I achieved with a ```grow()``` to the touchedupoutside :) – mpc75 Apr 14 '20 at 13:44