In a navigation controller embedded project, using animations, I cannot avoid getting a "fast preview" of the buttons present in the next ViewController, where those buttons will slide from left border of the screen to the center.
So, when I tap in first VC, see for an instant the buttons in second VC, and then the second VC appears and the animation works (and it works fine)
In home controller the button is:
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
print("tapped")
//animate 1/2
UIView.animate(withDuration: 0.8, animations: { () -> Void in
self.mainButtonContainerView.center.y -= self.view.bounds.width //self.view.frame.origin
self.mainButtonContainerView.isUserInteractionEnabled = false
self.mainButtonContainerView.alpha = 0.0
})
//animate2/2
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1) ) {
self.pushShowNextVC_ChoiceVC(dataToPass: "")
}
}
Then, the animation in second VC ViewDidLoad is:
for singleView in coantinersOutCollection {
//animation
singleView.center.x -= self.view.bounds.width
singleView.layer.cornerRadius = singleView.bounds.size.height/2
singleView.backgroundColor = UIColor.white
singleView.layer.borderColor = Constants.skyBlue3Color.cgColor
singleView.layer.borderWidth = 1
}
for singleLabel in labesForButtons {
singleLabel.textColor = Constants.skyBlue3Color
singleLabel.font = UIFont.boldSystemFont(ofSize: 17.0)
}
for singleImage in self.iconButtonImages {
// singleImage.image = UIImage(named: "")
singleImage.backgroundColor = UIColor.clear
singleImage.layer.borderWidth = 1
singleImage.layer.borderColor = Constants.skyBlue3Color.cgColor
}
UIView.animate(withDuration: 1.5, delay: 0.6,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerDownloadButton.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1.5, delay: 0.7,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerUploadButton.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1.5, delay: 0.8,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerCallButton.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1.5, delay: 0.9,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 1,
options: [], animations: {
self.containerIdleButton.center.x += self.view.bounds.width
}, completion: nil)
I added spring option to an UIImage. It worked fine on my phone, very nice. Bouncy and smooth.
But when I run it on the iOS simulators, it always leaves some edges on the screen.
It becomes a problem when I need to upload screenshots to App Store.
This is my code:
let theTile = numberTile
let bounds = theTile.bounds
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [.curveEaseInOut, .allowUserInteraction], animations: {
theTile.bounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width * 1.2, height: bounds.size.height * 1.2)
}, completion: nil)
This is the shrink back code
let theTile = numberTile
let bounds = theTile.bounds
UIView.animate(withDuration: 0.3, delay: 0, options: .allowUserInteraction, animations: {
theTile.bounds = CGRect(origin: bounds.origin, size: self.size)
}, completion: nil)
Try this although this might not be your only issue. If that is a collection view I would need to see your cell for row function as you may be loading a cell with the bounds messed up to start with. Here is a suggestion though. Animate the transform instead.
//to animate larger
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.2,initialSpringVelocity: 10, options: [.curveEaseInOut, .allowUserInteraction], animations: {
theTile.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}, completion: nil)
//animate smaller
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [.curveEaseInOut, .allowUserInteraction], animations: {
theTile.transform = .identity
}, completion: nil)
If this is a collection view in cell for row you may need to check for selected index but def if not selected you need to set the transform to identity.
EDIT:
You say this does not fix the issue. What environment are you running? Type of Mac. I personally believe it is a code issue somewhere else as I have written tons of animation code and never seen this bug. Here is a minimum example that does not create "ghost layers" on my device or simulator. If you can make a minimal example that does it would help a lot... Until then I think it is something in your code possibly your collection view.
import UIKit
class ViewController: UIViewController {
lazy var box : UIView = {
let v = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
v.backgroundColor = .blue
v.center = self.view.center
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .lightGray
self.view.addSubview(box)
box.layer.cornerRadius = 8
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
fireEvent()
super.touchesBegan(touches, with: event)
}
func fireEvent(){
if box.transform == .identity{
//to animate larger
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.2,initialSpringVelocity: 10, options: [.curveEaseInOut, .allowUserInteraction], animations: {
self.box.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}, completion: nil)
}else{
//animate smaller
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [.curveEaseInOut, .allowUserInteraction], animations: {
self.box.transform = .identity
}, completion: nil)
}
}
}
I have small background animation to change gradient, ex u can see here animation
As you see if i open app first time, animation is working, after the changing View Controller animation is stop.
My code:
func animateGrandient() {
UIView.animate(withDuration: 15, delay: 0, options: [.autoreverse, .curveLinear, .repeat], animations: {
let x = -(self.gradientView.frame.width - self.view.frame.width)
self.gradientView.transform = CGAffineTransform(translationX: x, y: 0)
})
}
And outlet:
#IBOutlet weak var gradientView: UIImageView!
This happens because your self.gradientView.transform is changed already before your animation is executed, so you need to reset your self.gradientView.transform
Add this line self.gradientView.transform = CGAffineTransform.identity in the beginning of that method
fixed code
func animateGrandient() {
self.gradientView.transform = CGAffineTransform.identity
UIView.animate(withDuration: 15, delay: 0, options: [.autoreverse, .curveLinear, .repeat], animations: {
let x = -(self.gradientView.frame.width - self.view.frame.width)
self.gradientView.transform = CGAffineTransform(translationX: x, y: 0)
})
}
I am learning swift, I was successfully able to do popup animation of an UIView as below code but now i want to do reverse of it. i.e the way the UIView was animated should go back on a button click, Like the view is shrinking and then disappears.
popupView.isHidden = false
popupInnerView.isHidden = false
popupInnerView.transform = CGAffineTransform(scaleX: 0, y: 0)
UIView.animate(withDuration: 1, delay: 0, options: .curveLinear, animations: {
self.popupView.alpha = 1.0;
self.popupInnerView.transform = .identity
}, completion: nil)
Check if this one works for you. I have bound the showing and hiding of popup to buttons but you can bind them to anything.
For this to work
Drag and drop a uiview(your popup) into dock of view controller (see picture)
Make outlet of your uiview(popup) in your view controller. I am calling it popupView
For showing popup with Animation use the code
#IBAction func btnShowPopupTapped(_ sender: UIButton) {
popupView.center = view.center
popupView.alpha = 1
popupView.transform = CGAffineTransform(scaleX: 0.8, y: 1.2)
self.view.addSubview(popupView)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: {
//use if you want to darken the background
//self.viewDim.alpha = 0.8
//go back to original form
self.popupView.transform = .identity
})
}
And for hiding the popup
#IBAction func btnHideMeTapped(_ sender: Any) {
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: {
//use if you wish to darken the background
//self.viewDim.alpha = 0
self.popupView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
}) { (success) in
self.popupView.removeFromSuperview()
}
}
Note: If you want to darken the background as in gif.
Drag and drop a uiview into your view heirarchy (view dim). see the pic
pin to 0, 0 , 0 ,0 to the main view
set its background color to black
set is alpha to 0
uncomment the line (self.viewDim.alpha = 0.8) in "btnShowPopupTapped" Action
uncomment the line (self.viewDim.alpha = 0) in "btnHideMeTapped" Action
Let me know if this helps.
Try This
For Zoom in
UIView.animate(withDuration: 1, delay: 0, options: .curveLinear, animations: {
self.popupInnerView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
}, completion: { _ in
self.popupInnerView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
})
For Zoom Out
UIView.animate(withDuration: 1, delay: 0, options: .curveLinear, animations: {
self.popupInnerView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
}, completion: nil)
This code worked for Pop Out
UIView.animate(withDuration: 1, delay: 0, options: .curveLinear, animations: {
self.popupView.alpha = 0.0;
self.popupInnerView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}, completion: { _ in
self.popupView.isHidden = true
self.popupInnerView.isHidden = true
})
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I'm trying to do scale animation for UIButton when its clicked but what I'm trying to accomplish is when the button clicked I need the UIButton to be smaller to the inside then it comes back to its same size (like a bubble).
I tried the following:
button.transform = CGAffineTransformMakeScale(-1, 1)
UIView.animateWithDuration(0.5, animations: { () -> Void in
button.transform = CGAffineTransformMakeScale(1,1)
})
Try this
UIView.animate(withDuration: 0.6,
animations: {
self.button.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
},
completion: { _ in
UIView.animate(withDuration: 0.6) {
self.button.transform = CGAffineTransform.identity
}
})
SWIFT 5 Code Update :I have animated button with a nice bouncing effect , with spring animation.
#IBOutlet weak var button: UIButton!
#IBAction func animateButton(sender: UIButton) {
sender.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
UIView.animate(withDuration: 2.0,
delay: 0,
usingSpringWithDamping: CGFloat(0.20),
initialSpringVelocity: CGFloat(6.0),
options: UIView.AnimationOptions.allowUserInteraction,
animations: {
sender.transform = CGAffineTransform.identity
},
completion: { Void in() }
)
}
All of the answers above are valid.
As a plus, with Swift I suggest to create an extension of UIView in order to "scale" any view you want.
You can take inspiration from this piece of code:
SWIFT 5.0
extension UIView {
/**
Simply zooming in of a view: set view scale to 0 and zoom to Identity on 'duration' time interval.
- parameter duration: animation duration
*/
func zoomIn(duration: TimeInterval = 0.2) {
self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
UIView.animate(withDuration: duration, delay: 0.0, options: [.curveLinear], animations: { () -> Void in
self.transform = .identity
}) { (animationCompleted: Bool) -> Void in
}
}
/**
Simply zooming out of a view: set view scale to Identity and zoom out to 0 on 'duration' time interval.
- parameter duration: animation duration
*/
func zoomOut(duration : TimeInterval = 0.2) {
self.transform = .identity
UIView.animate(withDuration: duration, delay: 0.0, options: [.curveLinear], animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
}) { (animationCompleted: Bool) -> Void in
}
}
/**
Zoom in any view with specified offset magnification.
- parameter duration: animation duration.
- parameter easingOffset: easing offset.
*/
func zoomInWithEasing(duration: TimeInterval = 0.2, easingOffset: CGFloat = 0.2) {
let easeScale = 1.0 + easingOffset
let easingDuration = TimeInterval(easingOffset) * duration / TimeInterval(easeScale)
let scalingDuration = duration - easingDuration
UIView.animate(withDuration: scalingDuration, delay: 0.0, options: .curveEaseIn, animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: easeScale, y: easeScale)
}, completion: { (completed: Bool) -> Void in
UIView.animate(withDuration: easingDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in
self.transform = .identity
}, completion: { (completed: Bool) -> Void in
})
})
}
/**
Zoom out any view with specified offset magnification.
- parameter duration: animation duration.
- parameter easingOffset: easing offset.
*/
func zoomOutWithEasing(duration: TimeInterval = 0.2, easingOffset: CGFloat = 0.2) {
let easeScale = 1.0 + easingOffset
let easingDuration = TimeInterval(easingOffset) * duration / TimeInterval(easeScale)
let scalingDuration = duration - easingDuration
UIView.animate(withDuration: easingDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: easeScale, y: easeScale)
}, completion: { (completed: Bool) -> Void in
UIView.animate(withDuration: scalingDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
}, completion: { (completed: Bool) -> Void in
})
})
}
}
Usage is very simply:
let button = UIButton(frame: frame)
button.zoomIn() // here the magic
Swift 3 Version
extension UIView {
/**
Simply zooming in of a view: set view scale to 0 and zoom to Identity on 'duration' time interval.
- parameter duration: animation duration
*/
func zoomIn(duration: TimeInterval = 0.2) {
self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
UIView.animate(withDuration: duration, delay: 0.0, options: [.curveLinear], animations: { () -> Void in
self.transform = CGAffineTransform.identity
}) { (animationCompleted: Bool) -> Void in
}
}
/**
Simply zooming out of a view: set view scale to Identity and zoom out to 0 on 'duration' time interval.
- parameter duration: animation duration
*/
func zoomOut(duration: TimeInterval = 0.2) {
self.transform = CGAffineTransform.identity
UIView.animate(withDuration: duration, delay: 0.0, options: [.curveLinear], animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
}) { (animationCompleted: Bool) -> Void in
}
}
/**
Zoom in any view with specified offset magnification.
- parameter duration: animation duration.
- parameter easingOffset: easing offset.
*/
func zoomInWithEasing(duration: TimeInterval = 0.2, easingOffset: CGFloat = 0.2) {
let easeScale = 1.0 + easingOffset
let easingDuration = TimeInterval(easingOffset) * duration / TimeInterval(easeScale)
let scalingDuration = duration - easingDuration
UIView.animate(withDuration: scalingDuration, delay: 0.0, options: .curveEaseIn, animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: easeScale, y: easeScale)
}, completion: { (completed: Bool) -> Void in
UIView.animate(withDuration: easingDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in
self.transform = CGAffineTransform.identity
}, completion: { (completed: Bool) -> Void in
})
})
}
/**
Zoom out any view with specified offset magnification.
- parameter duration: animation duration.
- parameter easingOffset: easing offset.
*/
func zoomOutWithEasing(duration: TimeInterval = 0.2, easingOffset: CGFloat = 0.2) {
let easeScale = 1.0 + easingOffset
let easingDuration = TimeInterval(easingOffset) * duration / TimeInterval(easeScale)
let scalingDuration = duration - easingDuration
UIView.animate(withDuration: easingDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: easeScale, y: easeScale)
}, completion: { (completed: Bool) -> Void in
UIView.animate(withDuration: scalingDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
}, completion: { (completed: Bool) -> Void in
})
})
}
}
Swift 3.x+
extension UIButton {
func pulsate() {
let pulse = CASpringAnimation(keyPath: "transform.scale")
pulse.duration = 0.2
pulse.fromValue = 0.95
pulse.toValue = 1.0
pulse.autoreverses = true
pulse.repeatCount = 2
pulse.initialVelocity = 0.5
pulse.damping = 1.0
layer.add(pulse, forKey: "pulse")
}
func flash() {
let flash = CABasicAnimation(keyPath: "opacity")
flash.duration = 0.2
flash.fromValue = 1
flash.toValue = 0.1
flash.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
flash.autoreverses = true
flash.repeatCount = 3
layer.add(flash, forKey: nil)
}
func shake() {
let shake = CABasicAnimation(keyPath: "position")
shake.duration = 0.05
shake.repeatCount = 2
shake.autoreverses = true
let fromPoint = CGPoint(x: center.x - 5, y: center.y)
let fromValue = NSValue(cgPoint: fromPoint)
let toPoint = CGPoint(x: center.x + 5, y: center.y)
let toValue = NSValue(cgPoint: toPoint)
shake.fromValue = fromValue
shake.toValue = toValue
layer.add(shake, forKey: "position")
}
}
Usage:
myButton.flash()
// myButton.pulsate()
// myButton.shake()
Credits: Sean Allen
Swift 3 Version:
UIView.animate(withDuration: 0.6, animations: {
button.transform = CGAffineTransform.identity.scaledBy(x: 0.6, y: 0.6)
}, completion: { (finish) in
UIView.animate(withDuration: 0.6, animations: {
button.transform = CGAffineTransform.identity
})
})
Using Swift 4 Xcode 9, This will animate the button down when initially pressed and then back up when released.
extension UIView {
func animateButtonDown() {
UIView.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .curveEaseIn], animations: {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
}, completion: nil)
}
func animateButtonUp() {
UIView.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .curveEaseOut], animations: {
self.transform = CGAffineTransform.identity
}, completion: nil)
}
Implementation:
#IBAction func buttonTouchDown(_ sender: UIButton) {
//Connected with Touch Down Action
sender.animateButtonDown()
}
#IBAction func buttonTouchUpOutside(_ sender: UIButton) {
//Connected with Touch Up Outside Action
//if touch moved away from button
sender.animateButtonUp()
}
#IBAction func buttonTouchUpInside(_ sender: UIButton) {
//Connected with Touch Up Inside Action
sender.animateButtonUp()
//code to execute when button pressed
}
It works with me as following, the animation is set to be small then when it start animation it get back to its original size:
Swift 2
button.transform = CGAffineTransformMakeScale(0.6, 0.6)
UIView.animateWithDuration(0.3, animations: { () -> Void in
button.transform = CGAffineTransformMakeScale(1,1)
})
Swift 3, 4, 5
button.transform = CGAffineTransform.init(scaleX: 0.6, y: 0.6)
UIView.animate(withDuration: 0.3, animations: { () -> Void in
button.transform = CGAffineTransform.init(scaleX: 1, y: 1)
})
I prefer to have the press animation and set it more fast than the other examples, with the completion control for waiting until the animation is ended:
Swift 3:
extension UIButton {
func press(completion:#escaping ((Bool) -> Void)) {
UIView.animate(withDuration: 0.05, animations: {
self.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) }, completion: { (finish: Bool) in
UIView.animate(withDuration: 0.1, animations: {
self.transform = CGAffineTransform.identity
completion(finish)
})
})
}
}
Usage:
#IBAction func playPauseBtnTap(_ sender: Any) {
let playPauseBtn = sender as! UIButton
playPauseBtn.press(completion:{ finish in
if finish {
print("animation ended")
}
}
}
Using the following animation the button will start from its full size, decrease to 0.6 with a spring animation to bounce back to it's full size.
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.4 initialSpringVelocity:0.3 options:0 animations:^{
//Animations
button.transform = CGAffineTransformIdentity;
CGAffineTransformMakeScale(0.6, 0.6)
} completion:^(BOOL finished) {
//Completion Block
[UIView.animateWithDuration(0.5){
button.transform = CGAffineTransformIdentity
}];
}];
You can try this if you want a Autoreverse effect with a completion handler.
viewToAnimate.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 0.7, // your duration
delay: 0,
usingSpringWithDamping: 0.2,
initialSpringVelocity: 6.0,
animations: { _ in
viewToAnimate.transform = .identity
},
completion: { _ in
// Implement your awesome logic here.
})
iOS 9 and xCode 7
//for zoom in
[UIView animateWithDuration:0.5f animations:^{
self.sendButton.transform = CGAffineTransformMakeScale(1.5, 1.5);
} completion:^(BOOL finished){}];
// for zoom out
[UIView animateWithDuration:0.5f animations:^{
self.sendButton.transform = CGAffineTransformMakeScale(1, 1);
}completion:^(BOOL finished){}];
This will give a wonderful bouncing effect:
#IBAction func TouchUpInsideEvent(sender: UIButton) {
UIView.animateWithDuration(2.0,
delay: 0,
usingSpringWithDamping: CGFloat(0.20),
initialSpringVelocity: CGFloat(6.0),
options: UIViewAnimationOptions.AllowUserInteraction,
animations: {
sender.transform = CGAffineTransformIdentity
},
completion: { Void in() }
)
}
#IBAction func touchDownEvent(sender: UIButton) {
UIView.animateWithDuration(0.15, animations: {
sender.transform = CGAffineTransformMakeScale(0.6, 0.6)
})
}
Scaling Button or any view about three times or more use following code. swift 3 or swift 4 with xcode 9.
UIView.animate(withDuration: 0.2, animations: {
self.cartShowHideBtnView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
}, completion: { (finish: Bool) in
UIView.animate(withDuration: 0.2, animations: {
self.cartShowHideBtnView.transform = CGAffineTransform.identity
}, completion:{(finish: Bool) in
UIView.animate(withDuration: 0.2, animations: {
self.cartShowHideBtnView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
}, completion: { (finish: Bool) in
UIView.animate(withDuration: 0.2, animations: {
self.cartShowHideBtnView.transform = CGAffineTransform.identity
}, completion:{(finish: Bool) in
UIView.animate(withDuration: 0.2, animations: {
self.cartShowHideBtnView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
}, completion: { (finish: Bool) in
UIView.animate(withDuration: 0.2, animations: {
self.cartShowHideBtnView.transform = CGAffineTransform.identity
})
})
})
})
})
})
I did a protocol using Swift 4, that you can use at some specifics UIViews that you want to animate... You can try some animations over here or change time and delay.
This way is recommended because you can use this protocol and others at one view and this view can use this functions, doing a lot os extensions from UIView create code smell.
import Foundation
import UIKit
protocol Showable where Self: UIView {}
extension Showable {
func show(_ view: UIView? = nil) {
if let view = view {
self.animate(view)
} else {
self.animate(self)
}
}
private func animate(_ view: UIView) {
view.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
UIView.animate(withDuration: 2.0,
delay: 0,
usingSpringWithDamping: CGFloat(0.20),
initialSpringVelocity: CGFloat(6.0),
options: [.allowUserInteraction],
animations: {
view.transform = CGAffineTransform.identity
})
}
}
Here is a working example :
extension UIButton{
func flash() {
let flash = CABasicAnimation(keyPath: "opacity")
flash.duration = 0.5
flash.fromValue = 1
flash.toValue = 0.1
flash.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
flash.autoreverses = true
flash.repeatCount = 3
layer.add(flash, forKey: nil)
}
}
#IBAction func taptosave(_ sender: UIButton) {
sender.flash()
}