Jumping effect on uibuttons - iphone

Here in my app i used the moving sub View.If it reach y=150,i want to make a buttons in jumping effect,i tried this link adding bounce effect to appearance of UIImageView it working in horizontal direction ,i want a vertical direction,Here my code,
-(void)godown
{
if (movingview.center.y < 150) movingview.center = CGPointMake(movingview.center.x, movingview.center.y +5);
if(movingview.center.y ==150)
{
[UIView beginAnimations:#"bounce" context:nil];
[UIView setAnimationRepeatCount:3];
[UIView setAnimationRepeatAutoreverses:YES];
CGAffineTransform transform = CGAffineTransformMakeScale(1.3,1.3);
bt1.transform = transform;
bt2.transform=transform;
[UIView commitAnimations];
}
}
Please help me to change into jumping effect(vertically)?

Try this. You can make some changes so it suits your need,
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.125;
anim.repeatCount = 1;
anim.autoreverses = YES;
anim.removedOnCompletion = YES;
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)];
[senderView.layer addAnimation:anim forKey:nil];
Hope this helps.

Swift 3
Here i found a useful post on this attractive pop animation. Hope it is what you want
#IBAction func btnCancel_click(_ sender: Any)
{
btnCancel.transform = CGAffineTransform(scaleX: 0.50, y: 0.50)
UIView.animate(withDuration: 2.0,
delay: 0,
usingSpringWithDamping: 0.2,
initialSpringVelocity: 6.0,
options: .allowUserInteraction,
animations: { [weak self] in
self?.btnCancel.transform = .identity
},
completion: nil)
}
source: http://evgenii.com/blog/spring-button-animation-with-swift/

Try this code..
- (IBAction)bounce {
CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.y"];///use transform
theAnimation.duration=0.4;
theAnimation.repeatCount=2;
theAnimation.autoreverses=YES;
theAnimation.fromValue=[NSNumber numberWithFloat:1.0];
theAnimation.toValue=[NSNumber numberWithFloat:-20];
[yourButton.layer addAnimation:theAnimation forKey:#"animateTranslation"];//animationkey
}
see the whole answer from this link

And the swift version
func jumpButtonAnimation(sender: UIButton) {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.toValue = NSNumber(float: 1.3)
animation.duration = 0.1
animation.repeatCount = 0
animation.autoreverses = true
sender.layer.addAnimation(animation, forKey: nil)
}

UIView.animateWithDuration(0.1 ,
animations: {
self.buttonName.transform = CGAffineTransformMakeScale(1.3, 1.3)
},
completion: { finish in
UIView.animateWithDuration(0.1){
self.buttonName.transform = CGAffineTransformIdentity
}
})

Related

Preserve Ripple effect over UIImageView

I want to preserve Ripple effect on my UIImageView. I know we can animate image for ripple effect , but preserve. In other words I want a rippled image.
I know we can animate image using
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:25.0];
[UIView setAnimationTransition:(UIViewAnimationTransition) 110 forView:imgRipple cache:NO];
[UIView commitAnimations];
But it animates , doesn't keep the ripple. I think we can get rippled image if we Pause or Stop animation before it ends. Is it possible? how can we Pause or Stop UIView Animation?
If there is any alternate to this trick ,kindly mention it.
Thanks.
I got my solution,
used this code
CFTimeInterval pausedTime = [imgRipple.layer convertTime:CACurrentMediaTime() fromLayer:nil];
imgRipple.layer.speed = 0.0;
imgRipple.layer.timeOffset = pausedTime;
and it paused
I'm not sure if it gives you what you want, but you can stop animations using those:
#import <QuartzCore/QuartzCore.h>
[CATransaction begin];
[myView.layer removeAllAnimations];
[CATransaction commit];
You can get the current state before stopping animation using presentationLayer:
CALayer* myPreLayer = [myView.layer presentationLayer];
CATransform3D currentTransform = [myPreLayer transform];
//if you need some specific info, you can use key-value pairs
float currentAngle = [[myPreLayer valueForKeyPath:#"transform.rotation.z"] floatValue];
swift 5
Imageview set ripple animation its working fine......
#IBOutlet weak var imageview: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageview.layer.cornerRadius = imageview.bounds.width / 2
self.animateImage()
}
func animateImage() {
addRippleEffect(to: viewAnimation)
}
func addRippleEffect(to referenceView: UIView) {
/*! Creates a circular path around the view*/
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: referenceView.bounds.size.width, height: referenceView.bounds.size.height))
/*! Position where the shape layer should be */
let shapePosition = CGPoint(x: referenceView.bounds.size.width / 2.0, y: referenceView.bounds.size.height / 2.0)
let rippleShape = CAShapeLayer()
rippleShape.bounds = CGRect(x: 0, y: 0, width: referenceView.bounds.size.width, height: referenceView.bounds.size.height)
rippleShape.path = path.cgPath
rippleShape.fillColor = UIColor.clear.cgColor
rippleShape.strokeColor = UIColor.black.cgColor
rippleShape.lineWidth = 5
rippleShape.position = shapePosition
rippleShape.opacity = 0
/*! Add the ripple layer as the sublayer of the reference view */
referenceView.layer.addSublayer(rippleShape)
/*! Create scale animation of the ripples */
let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
scaleAnim.fromValue = NSValue(caTransform3D: CATransform3DIdentity)
scaleAnim.toValue = NSValue(caTransform3D: CATransform3DMakeScale(2, 2, 1))
/*! Create animation for opacity of the ripples */
let opacityAnim = CABasicAnimation(keyPath: "opacity")
opacityAnim.fromValue = 1
opacityAnim.toValue = 0
/*! Group the opacity and scale animations */
let animation = CAAnimationGroup()
animation.animations = [scaleAnim, opacityAnim]
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.duration = CFTimeInterval(1.0)
animation.repeatCount = .infinity
animation.isRemovedOnCompletion = true
rippleShape.add(animation, forKey: "rippleEffect")
}

adding bounce effect to appearance of UIImageView

How can I add a bounce effect when I am about to show an UIImageView as a subview? Do I have to use CoreAnimation to do this? My only guess right now is to use CAKeyframeAnimation, please let me know if there's a better way. Here's my current code:
CABasicAnimation * theAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
theAnimation.delegate = self;
theAnimation.duration = 1.0;
theAnimation.fromValue = [NSNumber numberWithFloat:notif.center.y];
theAnimation.toValue = [NSNumber numberWithFloat:notif.center.y-20];
theAnimation.repeatCount = 3;
y-axis animation using CABasicAnimation:
CGPoint origin = self.imageView.center;
CGPoint target = CGPointMake(self.imageView.center.x, self.imageView.center.y+100);
CABasicAnimation *bounce = [CABasicAnimation animationWithKeyPath:#"position.y"];
bounce.duration = 0.5;
bounce.fromValue = [NSNumber numberWithInt:origin.y];
bounce.toValue = [NSNumber numberWithInt:target.y];
bounce.repeatCount = 2;
bounce.autoreverses = YES;
[self.imageView.layer addAnimation:bounce forKey:#"position"];
If you want to implement shrink and grow you have to add a CGAffineTransformMakeScale, eg:
// grow
CGAffineTransform transform = CGAffineTransformMakeScale(1.3, 1.3);
imageView.transform = transform;
Bouncy (expand/shrink) animation in Swift:
var selected: Bool {
willSet(selected) {
let expandTransform:CGAffineTransform = CGAffineTransformMakeScale(1.2, 1.2);
if (!self.selected && selected) {
self.imageView.image = SNStockCellSelectionAccessoryViewImage(selected)
self.imageView.transform = expandTransform
UIView.animateWithDuration(0.4,
delay:0.0,
usingSpringWithDamping:0.40,
initialSpringVelocity:0.2,
options: .CurveEaseOut,
animations: {
self.imageView.transform = CGAffineTransformInvert(expandTransform)
}, completion: {
//Code to run after animating
(value: Bool) in
})
}
}
}
var imageView:UIImageView
If imageView is correctly added to the view as a subview, toggling between selected = false to selected = true should swap the image with a bouncy animation. SNStockCellSelectionAccessoryViewImage just returns a different image based on the current selection state, see below:
private let SNStockCellSelectionAccessoryViewPlusIconSelected:UIImage = UIImage(named:"PlusIconSelected")!
private let SNStockCellSelectionAccessoryViewPlusIcon:UIImage = UIImage(named:"PlusIcon")!
private func SNStockCellSelectionAccessoryViewImage(selected:Bool) -> UIImage {
return selected ? SNStockCellSelectionAccessoryViewPlusIconSelected : SNStockCellSelectionAccessoryViewPlusIcon
}
The GIF example below is a bit slowed down, the actual animation happens faster:
[UIView animateWithDuration:0.8
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0.5
options:(UIViewAnimationOptionAutoreverse|
UIViewAnimationOptionRepeat)
animations:^{
CGRect frame = view.frame;
frame.origin.y -= 8;
view.frame = frame;
} completion:nil];
Play with the values to get different effects.
#Jano's answer in Swift
let origin:CGPoint = self.image.center
let target:CGPoint = CGPointMake(self.image.center.x, self.image.center.y+100)
let bounce = CABasicAnimation(keyPath: "position.y")
bounce.duration = 1
bounce.fromValue = origin.y
bounce.toValue = target.y
bounce.repeatCount = 2
bounce.autoreverses = true
self.image.layer.addAnimation(bounce, forKey: "position")
I followed this link
func imageViewBounceEffect() {
yourImageView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 1.35, delay: 0,
usingSpringWithDamping: 0.25,
initialSpringVelocity: 5,
options: .curveEaseOut,
animations: {
self.yourImageView.transform = .identity
})
}

iOS icon jiggle algorithm

I am writing an iPad app that presents user documents similar to the way Pages presents them (as large icons of the actual document). I also want to mimic the jiggling behavior when the user taps the edit button. This is the same jiggle pattern that icons make on the home screen when you tap and hold on them on both the iPhone and iPad.
I've searched the Internet and have found a few algorithms but they just cause the view to rock back and forth which is not at all like the Apple jiggle. It seems there is some randomness in there as each icon jiggles a little differently.
Does anyone have or know of some code that can re-create the same jiggle pattern (or something very close to it)? Thanks!!!
#Vic320's answer is good but personally I don't like the translation.
I've edited his code to provide a solution that I personally feel looks more like the springboard wobble effect. Mostly, it's achieved by adding a little randomness and focusing on rotation, without translation:
#define degreesToRadians(x) (M_PI * (x) / 180.0)
#define kAnimationRotateDeg 1.0
- (void)startJiggling {
NSInteger randomInt = arc4random_uniform(500);
float r = (randomInt/500.0)+0.5;
CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians( (kAnimationRotateDeg * -1.0) - r ));
CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg + r ));
self.transform = leftWobble; // starting point
[[self layer] setAnchorPoint:CGPointMake(0.5, 0.5)];
[UIView animateWithDuration:0.1
delay:0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^{
[UIView setAnimationRepeatCount:NSNotFound];
self.transform = rightWobble; }
completion:nil];
}
- (void)stopJiggling {
[self.layer removeAllAnimations];
self.transform = CGAffineTransformIdentity;
}
Credit where credit's due though, #Vic320's answer provided the basis for this code so +1 for that.
OK, so the openspringboard code didn't quite do it for me but I did allow me to create some code that I think is a bit better, still not prefect but better. If anyone has suggestions to make this better, I would love to hear them... (add this to the subclass of the view(s) you want to jiggle)
#define degreesToRadians(x) (M_PI * (x) / 180.0)
#define kAnimationRotateDeg 1.0
#define kAnimationTranslateX 2.0
#define kAnimationTranslateY 2.0
- (void)startJiggling:(NSInteger)count {
CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg * (count%2 ? +1 : -1 ) ));
CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg * (count%2 ? -1 : +1 ) ));
CGAffineTransform moveTransform = CGAffineTransformTranslate(rightWobble, -kAnimationTranslateX, -kAnimationTranslateY);
CGAffineTransform conCatTransform = CGAffineTransformConcat(rightWobble, moveTransform);
self.transform = leftWobble; // starting point
[UIView animateWithDuration:0.1
delay:(count * 0.08)
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^{ self.transform = conCatTransform; }
completion:nil];
}
- (void)stopJiggling {
[self.layer removeAllAnimations];
self.transform = CGAffineTransformIdentity; // Set it straight
}
#mientus Original Apple Jiggle code in Swift 4, with optional parameters to adjust the duration (i.e. speed), displacement (i.e. position change) and degrees (i.e. rotation amount).
private func degreesToRadians(_ x: CGFloat) -> CGFloat {
return .pi * x / 180.0
}
func startWiggle(
duration: Double = 0.25,
displacement: CGFloat = 1.0,
degreesRotation: CGFloat = 2.0
) {
let negativeDisplacement = -1.0 * displacement
let position = CAKeyframeAnimation.init(keyPath: "position")
position.beginTime = 0.8
position.duration = duration
position.values = [
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: 0, y: 0)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: 0)),
NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement))
]
position.calculationMode = "linear"
position.isRemovedOnCompletion = false
position.repeatCount = Float.greatestFiniteMagnitude
position.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
position.isAdditive = true
let transform = CAKeyframeAnimation.init(keyPath: "transform")
transform.beginTime = 2.6
transform.duration = duration
transform.valueFunction = CAValueFunction(name: kCAValueFunctionRotateZ)
transform.values = [
degreesToRadians(-1.0 * degreesRotation),
degreesToRadians(degreesRotation),
degreesToRadians(-1.0 * degreesRotation)
]
transform.calculationMode = "linear"
transform.isRemovedOnCompletion = false
transform.repeatCount = Float.greatestFiniteMagnitude
transform.isAdditive = true
transform.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
self.layer.add(position, forKey: nil)
self.layer.add(transform, forKey: nil)
}
Paul Popiel gave an excellent answer to this above and I am forever indebted to him for it. There is one small problem I found with his code and that's that it doesn't work well if that routine is called multiple times - the layer animations appear to sometimes get lost or deactivated.
Why call it more than once? I'm implementing it via a UICollectionView, and as the cells are dequeued or moved, I need to reestablish the wiggle. With Paul's original code, my cells would often stop wiggling if they scrolled off screen despite my trying to reestablish the wiggle within the dequeue and the willDisplay callback. However, by giving the two animations named keys, it always works reliably even if called twice on a cell.
This is almost all Paul's code with the above small fix, plus I've created it as an extension to UIView and added a Swift 4 compatible stopWiggle.
private func degreesToRadians(_ x: CGFloat) -> CGFloat {
return .pi * x / 180.0
}
extension UIView {
func startWiggle() {
let duration: Double = 0.25
let displacement: CGFloat = 1.0
let degreesRotation: CGFloat = 2.0
let negativeDisplacement = -1.0 * displacement
let position = CAKeyframeAnimation.init(keyPath: "position")
position.beginTime = 0.8
position.duration = duration
position.values = [
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: 0, y: 0)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: 0)),
NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement))
]
position.calculationMode = "linear"
position.isRemovedOnCompletion = false
position.repeatCount = Float.greatestFiniteMagnitude
position.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
position.isAdditive = true
let transform = CAKeyframeAnimation.init(keyPath: "transform")
transform.beginTime = 2.6
transform.duration = duration
transform.valueFunction = CAValueFunction(name: kCAValueFunctionRotateZ)
transform.values = [
degreesToRadians(-1.0 * degreesRotation),
degreesToRadians(degreesRotation),
degreesToRadians(-1.0 * degreesRotation)
]
transform.calculationMode = "linear"
transform.isRemovedOnCompletion = false
transform.repeatCount = Float.greatestFiniteMagnitude
transform.isAdditive = true
transform.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
self.layer.add(position, forKey: "bounce")
self.layer.add(transform, forKey: "wiggle")
}
func stopWiggle() {
self.layer.removeAllAnimations()
self.transform = .identity
}
}
In case it saves anyone else time implementing this in a UICollectionView, you'll need a few other places to make sure the wiggle stays during moves and scrolls. First, a routine that begins wiggling all the cells that's called at the outset:
func wiggleAllVisibleCells() {
if let visible = collectionView?.indexPathsForVisibleItems {
for ip in visible {
if let cell = collectionView!.cellForItem(at: ip) {
cell.startWiggle()
}
}
}
}
And as new cells are displayed (from a move or scroll), I reestablish the wiggle:
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// Make sure cells are all still wiggling
if isReordering {
cell.startWiggle()
}
}
For completeness, here is how I animated my CALayer subclass — inspired by the other answers — using an explicit animation.
-(void)stopJiggle
{
[self removeAnimationForKey:#"jiggle"];
}
-(void)startJiggle
{
const float amplitude = 1.0f; // degrees
float r = ( rand() / (float)RAND_MAX ) - 0.5f;
float angleInDegrees = amplitude * (1.0f + r * 0.1f);
float animationRotate = angleInDegrees / 180. * M_PI; // Convert to radians
NSTimeInterval duration = 0.1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.duration = duration;
animation.additive = YES;
animation.autoreverses = YES;
animation.repeatCount = FLT_MAX;
animation.fromValue = #(-animationRotate);
animation.toValue = #(animationRotate);
animation.timeOffset = ( rand() / (float)RAND_MAX ) * duration;
[self addAnimation:animation forKey:#"jiggle"];
}
I reverse engineered Apple Stringboard, and modified little bit their animation, and code below is really good stuff.
+ (CAAnimationGroup *)jiggleAnimation {
CAKeyframeAnimation *position = [CAKeyframeAnimation animation];
position.keyPath = #"position";
position.values = #[
[NSValue valueWithCGPoint:CGPointZero],
[NSValue valueWithCGPoint:CGPointMake(-1, 0)],
[NSValue valueWithCGPoint:CGPointMake(1, 0)],
[NSValue valueWithCGPoint:CGPointMake(-1, 1)],
[NSValue valueWithCGPoint:CGPointMake(1, -1)],
[NSValue valueWithCGPoint:CGPointZero]
];
position.timingFunctions = #[
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]
];
position.additive = YES;
CAKeyframeAnimation *rotation = [CAKeyframeAnimation animation];
rotation.keyPath = #"transform.rotation";
rotation.values = #[
#0,
#0.03,
#0,
[NSNumber numberWithFloat:-0.02]
];
rotation.timingFunctions = #[
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]
];
CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.animations = #[ position, rotation ];
group.duration = 0.3;
group.repeatCount = HUGE_VALF;
group.beginTime = arc4random() % 30 / 100.f;
return group;
}
Original Apple jiggle animation:
CAKeyframeAnimation *position = [CAKeyframeAnimation animation];
position.beginTime = 0.8;
position.duration = 0.25;
position.values = #[[NSValue valueWithCGPoint:CGPointMake(-1, -1)],
[NSValue valueWithCGPoint:CGPointMake(0, 0)],
[NSValue valueWithCGPoint:CGPointMake(-1, 0)],
[NSValue valueWithCGPoint:CGPointMake(0, -1)],
[NSValue valueWithCGPoint:CGPointMake(-1, -1)]];
position.calculationMode = #"linear";
position.removedOnCompletion = NO;
position.repeatCount = CGFLOAT_MAX;
position.beginTime = arc4random() % 25 / 100.f;
position.additive = YES;
position.keyPath = #"position";
CAKeyframeAnimation *transform = [CAKeyframeAnimation animation];
transform.beginTime = 2.6;
transform.duration = 0.25;
transform.valueFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ];
transform.values = #[#(-0.03525565),#(0.03525565),#(-0.03525565)];
transform.calculationMode = #"linear";
transform.removedOnCompletion = NO;
transform.repeatCount = CGFLOAT_MAX;
transform.additive = YES;
transform.beginTime = arc4random() % 25 / 100.f;
transform.keyPath = #"transform";
[self.dupa.layer addAnimation:position forKey:nil];
[self.dupa.layer addAnimation:transform forKey:nil];
So I'm sure I'll get yelled at for writing messy code (there are probably simpler ways to do this that I am not aware of because I'm a semi-beginner), but this is a more random version of Vic320's algorithm that varies the amount of rotation and translation. It also decides randomly which direction it will wobble first, which gives a much more random look if you have multiple things wobbling simultaneously. If efficiency is a big problem for you, do not use. This is just what I came up with with the way that I know how to do it.
For anyone wondering you need to #import <QuartzCore/QuartzCore.h> and add it to your linked libraries.
#define degreesToRadians(x) (M_PI * (x) / 180.0)
- (void)startJiggling:(NSInteger)count {
double kAnimationRotateDeg = (double)(arc4random()%5 + 5) / 10;
double kAnimationTranslateX = (arc4random()%4);
double kAnimationTranslateY = (arc4random()%4);
CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg * (count%2 ? +1 : -1 ) ));
CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg * (count%2 ? -1 : +1 ) ));
int leftOrRight = (arc4random()%2);
if (leftOrRight == 0){
CGAffineTransform moveTransform = CGAffineTransformTranslate(rightWobble, -kAnimationTranslateX, -kAnimationTranslateY);
CGAffineTransform conCatTransform = CGAffineTransformConcat(rightWobble, moveTransform);
self.transform = leftWobble; // starting point
[UIView animateWithDuration:0.1
delay:(count * 0.08)
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^{ self.transform = conCatTransform; }
completion:nil];
} else if (leftOrRight == 1) {
CGAffineTransform moveTransform = CGAffineTransformTranslate(leftWobble, -kAnimationTranslateX, -kAnimationTranslateY);
CGAffineTransform conCatTransform = CGAffineTransformConcat(leftWobble, moveTransform);
self.transform = rightWobble; // starting point
[UIView animateWithDuration:0.1
delay:(count * 0.08)
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^{ self.transform = conCatTransform; }
completion:nil];
}
}
- (void)stopJiggling {
[self.layer removeAllAnimations];
self.transform = CGAffineTransformIdentity; // Set it straight
}
Check out the openspringboard project.
In particular, setIconAnimation:(BOOL)isAnimating in OpenSpringBoard.m. That should give you some ideas on how to do this.
For the benefit of others who come along in the future, I felt the jiggle offered by #Vic320 was a little too robotic and comparing it to Keynote it was a little too strong and not organic (random?) enough. So in the spirit of sharing, here is the code I built into my subclass of UIView... my view controller keeps an array of these objects and when the user taps the Edit button, the view controller sends the startJiggling message to each, followed by a stopJiggling message when the user presses the Done button.
- (void)startJiggling
{
// jiggling code based off the folks on stackoverflow.com:
// http://stackoverflow.com/questions/6604356/ios-icon-jiggle-algorithm
#define degreesToRadians(x) (M_PI * (x) / 180.0)
#define kAnimationRotateDeg 0.1
jiggling = YES;
[self wobbleLeft];
}
- (void)wobbleLeft
{
if (jiggling) {
NSInteger randomInt = arc4random()%500;
float r = (randomInt/500.0)+0.5;
CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians( (kAnimationRotateDeg * -1.0) - r ));
CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg + r ));
self.transform = leftWobble; // starting point
[UIView animateWithDuration:0.1
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{ self.transform = rightWobble; }
completion:^(BOOL finished) { [self wobbleRight]; }
];
}
}
- (void)wobbleRight
{
if (jiggling) {
NSInteger randomInt = arc4random()%500;
float r = (randomInt/500.0)+0.5;
CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians( (kAnimationRotateDeg * -1.0) - r ));
CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg + r ));
self.transform = rightWobble; // starting point
[UIView animateWithDuration:0.1
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{ self.transform = leftWobble; }
completion:^(BOOL finished) { [self wobbleLeft]; }
];
}
}
- (void)stopJiggling
{
jiggling = NO;
[self.layer removeAllAnimations];
[self setTransform:CGAffineTransformIdentity];
[self.layer setAnchorPoint:CGPointMake(0.5, 0.5)];
}
Here is the Swift 4.2 version of #mientus' code (which is itself an update of Paul Popiel's version), as an extension of CALayer:
extension CALayer {
private enum WigglingAnimationKey: String {
case position = "wiggling_position_animation"
case transform = "wiggling_transform_animation"
}
func startWiggling() {
let duration = 0.25
let displacement = 1.0
let negativeDisplacement = displacement * -1
let rotationAngle = Measurement(value: 2, unit: UnitAngle.degrees)
// Position animation
let positionAnimation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.position))
positionAnimation.beginTime = 0.8
positionAnimation.duration = duration
positionAnimation.values = [
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint.zero),
NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement))
]
positionAnimation.calculationMode = .linear
positionAnimation.isRemovedOnCompletion = false
positionAnimation.repeatCount = .greatestFiniteMagnitude
positionAnimation.beginTime = CFTimeInterval(Float(Int.random(in: 0...25)) / 100)
positionAnimation.isAdditive = true
// Rotation animation
let transformAnimation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.transform))
transformAnimation.beginTime = 2.6
transformAnimation.duration = duration
transformAnimation.valueFunction = CAValueFunction(name: .rotateZ)
transformAnimation.values = [
CGFloat(rotationAngle.converted(to: .radians).value * -1),
CGFloat(rotationAngle.converted(to: .radians).value),
CGFloat(rotationAngle.converted(to: .radians).value * -1)
]
transformAnimation.calculationMode = .linear
transformAnimation.isRemovedOnCompletion = false
transformAnimation.repeatCount = .greatestFiniteMagnitude
transformAnimation.isAdditive = true
transformAnimation.beginTime = CFTimeInterval(Float(Int.random(in: 0...25)) / 100)
self.add(positionAnimation, forKey: WigglingAnimationKey.position.rawValue)
self.add(transformAnimation, forKey: WigglingAnimationKey.transform.rawValue)
}
func stopWiggling() {
self.removeAnimation(forKey: WigglingAnimationKey.position.rawValue)
self.removeAnimation(forKey: WigglingAnimationKey.transform.rawValue)
}
}
Usage (where anyLayer is a CALayer):
// Start animating.
anyLayer.startWiggling()
// Stop animating.
anyLayer.stopWiggling()
In case anyone needs the same code in Swift
class Animation {
static func wiggle(_ btn: UIButton) {
btn.startWiggling()
}
}
extension UIView {
func startWiggling() {
let count = 5
let kAnimationRotateDeg = 1.0
let leftDegrees = (kAnimationRotateDeg * ((count%2 > 0) ? +5 : -5)).convertToDegrees()
let leftWobble = CGAffineTransform(rotationAngle: leftDegrees)
let rightDegrees = (kAnimationRotateDeg * ((count%2 > 0) ? -10 : +10)).convertToDegrees()
let rightWobble = CGAffineTransform(rotationAngle: rightDegrees)
let moveTransform = rightWobble.translatedBy(x: -2.0, y: 2.0)
let concatTransform = rightWobble.concatenating(moveTransform)
self.transform = leftWobble
UIView.animate(withDuration: 0.1,
delay: 0.1,
options: [.allowUserInteraction, .repeat, .autoreverse],
animations: {
UIView.setAnimationRepeatCount(3)
self.transform = concatTransform
}, completion: { success in
self.layer.removeAllAnimations()
self.transform = .identity
})
}
}
Just Call
Animation.wiggle(viewToBeAnimated)
It is always best to write a wrapper over the functions you are calling so that even if you have to change the function arguments or may be the name of the function, it does not take you to rewrite it everywhere in the code.

How to animate layer shadowOpacity?

I have a view on which I've set the layerOpacity to 1.
theView.layer.shadowOpacity = 1.0;
This looks fine when the view is farther down the screen. When I move this view up to be flush with another view that has a shadow, they don't look good. Is there a way I can animate the shadowOpacity on my layer to be 0? I tried using an animation block but it seems as if this property is not animatable.
EDIT: Request for code that doesn't work:
[UIView animateWithDuration:1.0 animations:^{
splitView2.layer.shadowOpacity = 0;}
completion:NULL];
This will work properly:
#import <QuartzCore/CAAnimation.h>
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"shadowOpacity"];
anim.fromValue = [NSNumber numberWithFloat:1.0];
anim.toValue = [NSNumber numberWithFloat:0.0];
anim.duration = 1.0;
[vv.layer addAnimation:anim forKey:#"shadowOpacity"];
vv.layer.shadowOpacity = 0.0;
For Swift 3.0:
let animation = CABasicAnimation(keyPath: "shadowOpacity")
animation.fromValue = layer.shadowOpacity
animation.toValue = 0.0
animation.duration = 1.0
view.layer.add(animation, forKey: animation.keyPath)
view.layer.shadowOpacity = 0.0
I've put above code in a little extension of UIView:
extension UIView {
func animateLayer<Value>(_ keyPath: WritableKeyPath<CALayer, Value>, to value:Value, duration: CFTimeInterval) {
let keyString = NSExpression(forKeyPath: keyPath).keyPath
let animation = CABasicAnimation(keyPath: keyString)
animation.fromValue = self.layer[keyPath: keyPath]
animation.toValue = value
animation.duration = duration
self.layer.add(animation, forKey: animation.keyPath)
var thelayer = layer
thelayer[keyPath: keyPath] = value
}
}
Usage like:
animateLayer(\.shadowOffset, to: CGSize(width: 3, height: 3), duration:1)
animateLayer(\.shadowOpacity, to: 0.4, duration: 1)
It's not thoroughly tested. but worked for me.
(Also posted here)
The below code work for me
1)Add QuartzCore frame work
2)Import QuartzCore frame work
Add the following Code in the required place
UIImageView * glowimageview = [[[UIImageView alloc]init]autorelease];
[glowimageview setFrame:CGRectMake(500,400,200,200)];
[glowimageview setImage:[UIImage imageNamed:#"144.png"]];
[sender addSubview:glowimageview];
glowimageview.layer.shadowColor = [UIColor redColor].CGColor;
glowimageview.layer.shadowRadius = 10.0f;
glowimageview.layer.shadowOpacity = 1.0f;
glowimageview.layer.shadowOffset = CGSizeZero;
CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:#"shadowOpacity"];
shadowAnimation.duration=1.0;
shadowAnimation.repeatCount=HUGE_VALF;
shadowAnimation.autoreverses=YES;
shadowAnimation.fromValue = [NSNumber numberWithFloat:1.0];
shadowAnimation.toValue = [NSNumber numberWithFloat:0.0];
[glowimageview.layer addAnimation:shadowAnimation forKey:#"shadowOpacity"];
It will works. Change the format of the code as per your requirement
Here's a material design like take on some of the above with animations
It's also available here as framework through Carthage https://github.com/sevenapps/SVNMaterialButton
public init(frame: CGRect, color: UIColor) {
super.init(frame: frame)
self.backgroundColor = color
self.layer.masksToBounds = false
self.layer.borderWidth = 1.0
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.8
self.layer.shadowRadius = 8
self.layer.shadowOffset = CGSize(width: 8.0, height: 8.0)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("This class is not set up to be instaciated with coder use init(frame) instead")
}
public override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.frame.height / 4
}
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.animate(to: 0.5, and: CGSize(width: 5.0, height: 5.0), with: 0.5)
super.touchesBegan(touches, with: event)
}
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.animate(to: 0.8, and: CGSize(width: 8.0, height: 8.0), with: 0.5)
super.touchesBegan(touches, with: event)
}
private func animate(to opacity: Double, and offset: CGSize, with duration: Double){
CATransaction.begin()
let opacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
opacityAnimation.toValue = opacity
opacityAnimation.duration = duration
opacityAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
opacityAnimation.fillMode = kCAFillModeBoth
opacityAnimation.isRemovedOnCompletion = false
let offsetAnimation = CABasicAnimation(keyPath: "shadowOffset")
offsetAnimation.toValue = offset
offsetAnimation.duration = duration
offsetAnimation.timingFunction = opacityAnimation.timingFunction
offsetAnimation.fillMode = opacityAnimation.fillMode
offsetAnimation.isRemovedOnCompletion = false
self.layer.add(offsetAnimation, forKey: offsetAnimation.keyPath!)
self.layer.add(opacityAnimation, forKey: opacityAnimation.keyPath!)
CATransaction.commit()
}

UITabBarItem Icon Animation

Skype app for iPhone uses animated TabBar icons. For example, during the logging-in the rightmost tab icon shows circulating arrows. While calling the "Call" tab icon softly blinks which is obviously done through animation.
I wonder how is it possible to animate tab bar items' icons.
In my particular case when the user presses the 'Favorite' button it jumps onto the 'Favorites' tab bar item. I have already implemented the jumping animation, but I would like the corresponding tab bar icon to blink at the end of animation to bring the feeling of completeness to it.
Any suggestions about the direction I should look in?
Thanks in advance.
I am surprised how easy the solution was!
Add method to your Application Delegate class .m-file (or any other class that manages your UITabBar) containing the following routine:
Create an UIImageView that will be used for animation.
Add it to your TabBar view using the addSubview: method.
Frame it down to the size of UITabBarItem (use UITabBar frame size and the number of tab bar items to calculate the frame size).
Adjust the imageView's frame.origin.x value to place the Image right above the tab bat item you want to animate.
Add animation you want to the imageView (you can play with opacity, swap several images - anything you want).
Pretty easy, don't you think so?
You can call this method on UIApplicationDelegate instance anywhere you need to animate the tab bar item.
Also it is important to notice that you can tap THROUGH the imageView to select the tab bar item as if there was no image over the tab bar. Many interesting conclusions can be done here on what you can do if you know it...
I have found a better solution to this problem. Adding custom image view is not a better approach because in iOS 10.0 and later on changing the orientation the icon frame & text position changed. You just need to set the class of UITabBarController & put the following code.
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let index = self.tabBar.items?.index(of: item)
let subView = tabBar.subviews[index!+1].subviews.first as! UIImageView
self.performSpringAnimation(imgView: subView)
}
//func to perform spring animation on imageview
func performSpringAnimation(imgView: UIImageView) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
imgView.transform = CGAffineTransform.init(scaleX: 1.4, y: 1.4)
//reducing the size
UIView.animate(withDuration: 0.5, delay: 0.2, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
imgView.transform = CGAffineTransform.init(scaleX: 1, y: 1)
}) { (flag) in
}
}) { (flag) in
}
}
Keep in mind that the order of the subviews in the UITabBar may be unordered so accessing the correct item from it using an index may not work correctly as Naresh answer suggested. Have a look at this post UITabBar subviews change order
The solution I found is to first filter and sort the views and then access it using the correct index of the tabBarItem.
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let orderedTabBarItemViews: [UIView] = {
let interactionViews = tabBar.subviews.filter({ $0 is UIControl })
return interactionViews.sorted(by: { $0.frame.minX < $1.frame.minX })
}()
guard
let index = tabBar.items?.firstIndex(of: item),
let subview = orderedTabBarItemViews[index].subviews.first
else {
return
}
performSpringAnimation(for: subview)
}
func performSpringAnimation(for view: UIView) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
view.transform = CGAffineTransform(scaleX: 1.25, y: 1.25)
UIView.animate(withDuration: 0.5, delay: 0.2, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
view.transform = CGAffineTransform(scaleX: 1, y: 1)
}, completion: nil)
}, completion: nil)
}
Actually there is much easier way: https://medium.com/#werry_paxman/bring-your-uitabbar-to-life-animating-uitabbaritem-images-with-swift-and-coregraphics-d3be75eb8d4d#.8o1raapyr
You can animate tabbar icons by getting its view, then do whatever animation as you like for the UIView. Below is a simple example with scale transform, cheer!
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem){
var tabBarView: [UIView] = []
for i in tabBar.subviews {
if i.isKind(of: NSClassFromString("UITabBarButton")! ) {
tabBarView.append(i)
}
}
if !tabBarView.isEmpty {
UIView.animate(withDuration: 0.15, animations: {
tabBarView[item.tag].transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}, completion: { _ in
UIView.animate(withDuration: 0.15) {
tabBarView[item.tag].transform = CGAffineTransform.identity
}
})
}
}
ps: please assign tag for each UITabBarItem in order
Index of UIImageView in subviews is not guaranteed as fist.
The index of UIImageView is not guaranteed as fist in related subviews.
So it is better to access it via class type, and also check for index out of bounds.
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
guard let idx = tabBar.items?.firstIndex(of: item),
tabBar.subviews.count > idx + 1,
let imageView = tabBar.subviews[idx + 1].subviews.compactMap({ $0 as? UIImageView }).first else {
return
}
imageView.layer.add(bounceAnimation, forKey: nil)
}
This is a sample basic bounce animation for that using CAKeyframeAnimation:
private var bounceAnimation: CAKeyframeAnimation = {
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
bounceAnimation.values = [1.0, 1.3, 0.9, 1.0]
bounceAnimation.duration = TimeInterval(0.3)
bounceAnimation.calculationMode = CAAnimationCalculationMode.cubic
return bounceAnimation
}()
Simple way to animate tab bar item in objective c
In your tabbar controller class
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
UIVisualEffectView *viv = tabBar.subviews[item.tag].subviews.firstObject;
UIImageView *img = viv.contentView.subviews.firstObject;
// If UIImageView not get use this code
// UIImageView *img = tabBar.subviews[item.tag].subviews.lastObject;
[self shakeAnimation:img];
//[self bounceAnimation:img];
}
- (void)shakeAnimation:(UIImageView *)img
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setDuration:0.05];
[animation setRepeatCount:2];
[animation setAutoreverses:YES];
[animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([img center].x - 10.0f, [img center].y)]];
[animation setToValue:[NSValue valueWithCGPoint: CGPointMake([img center].x + 10.0f, [img center].y)]];
[[img layer] addAnimation:animation forKey:#"position"];
}
- (void)bounceAnimation:(UIImageView *)img
{
img.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
[UIView animateWithDuration:0.3/1.5 animations:^{
img.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3/2 animations:^{
img.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.9, 0.9);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3/2 animations:^{
img.transform = CGAffineTransformIdentity;
}];
}];
}];
}
I haven't done that but I would just try to build a CAAnimation e.g. with a CABasicAnimation and add it to the UITabBarItem you want to be animated.
For details about how to set up a CABasicAnimation see the Core Animation Programming Guide:
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html#//apple_ref/doc/uid/TP40006085-SW1