How to create iphone distortion effect?

I want to rotate the image back and forth in my application, just like the iPhone icons swing when you click on it. What is the best way to do this?

This is my first foray into an animation that does not use animated GIFs. I think the idea is to slightly rotate the image back and forth to create a swell effect. I reviewed the use of CABasicAnimation and CAKeyframeAnimation. CABasicAnimation creates a jitter every time it repeats because it jumps to a position and does not interpolate back. CAKeyframeAnimation seems to be a solution, except that I cannot get it to work. I have to miss something. Here is my code using CAKeyframeAnimation (which doesn't work):

NSString *keypath = @"wobbleImage"; CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath]; animation.duration = 1.0f; animation.delegate = self; animation.repeatCount = 5; CGFloat wobbleAngle = 0.0872664626f; NSValue *initial = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0f, 0.0f, 0.0f, 1.0f)]; NSValue *middle = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)]; NSValue *final = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)]; animation.values = [NSArray arrayWithObjects:initial, middle, final, nil]; [imageView.layer addAnimation:animation forKey:keypath]; 


Or maybe a very simple solution that I just skipped. Appreciate any pointers. Thank!

+52
ios iphone rotation animation
May 30 '09 at 8:43 a.m.
source share
11 answers

A simple way to do this:

 #define RADIANS(degrees) (((degrees) * M_PI) / 180.0) CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5.0)); CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5.0)); itemView.transform = leftWobble; // starting point [UIView beginAnimations:@"wobble" context:itemView]; [UIView setAnimationRepeatAutoreverses:YES]; // important [UIView setAnimationRepeatCount:10]; [UIView setAnimationDuration:0.25]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(wobbleEnded:finished:context:)]; itemView.transform = rightWobble; // end here & auto-reverse [UIView commitAnimations]; ... - (void) wobbleEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { if ([finished boolValue]) { UIView* item = (UIView *)context; item.transform = CGAffineTransformIdentity; } } 

You probably need to play with timing and angles, but that should get you started.

EDIT: I edited the answer to add code to return the item to its original state. Also note that you can use the beginAnimations context beginAnimations to pass something to the start / stop methods. In this case, it is the swinging object itself, so you do not need to rely on specific ivars, and this method can be used for any general object based on UIView (i.e.Text tags, images, etc.).

+92
May 30 '09 at 16:54
source share

Ramin's answer was very good, but since OS4 the same effect can be achieved using animateWithDuration in one simple function.

(adapted his example for future googlers)

 #define RADIANS(degrees) (((degrees) * M_PI) / 180.0) - (void)startWobble { itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5)); [UIView animateWithDuration:0.25 delay:0.0 options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse) animations:^ { itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5)); } completion:NULL ]; } - (void)stopWobble { [UIView animateWithDuration:0.25 delay:0.0 options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear) animations:^ { itemView.transform = CGAffineTransformIdentity; } completion:NULL ]; } 
+92
Apr 19 '11 at 20:29
source share

You should use CAKeyframeAnimation to create smoother animations.

 + (void) animationKeyFramed: (CALayer *) layer delegate: (id) object forKey: (NSString *) key { CAKeyframeAnimation *animation; animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.duration = 0.4; animation.cumulative = YES; animation.repeatCount = 2; animation.values = [NSArray arrayWithObjects: [NSNumber numberWithFloat: 0.0], [NSNumber numberWithFloat: RADIANS(-9.0)], [NSNumber numberWithFloat: 0.0], [NSNumber numberWithFloat: RADIANS(9.0)], [NSNumber numberWithFloat: 0.0], nil]; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; animation.removedOnCompletion = NO; animation.delegate = object; [layer addAnimation:animation forKey:key]; } 
+14
Jan 07 2018-11-11T00:
source share

I wrote an example application that tries to replicate the wobble of the home screen and the movement of icons: iPhone code example: Tiles

+9
Oct 18 '11 at 23:22
source share

For those who recently met this post and would like to do the same in Swift, here is my translation:

 func smoothJiggle() { let degrees: CGFloat = 5.0 let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z") animation.duration = 0.6 animation.cumulative = true animation.repeatCount = Float.infinity animation.values = [0.0, degreesToRadians(-degrees) * 0.25, 0.0, degreesToRadians(degrees) * 0.5, 0.0, degreesToRadians(-degrees), 0.0, degreesToRadians(degrees), 0.0, degreesToRadians(-degrees) * 0.5, 0.0, degreesToRadians(degrees) * 0.25, 0.0] animation.fillMode = kCAFillModeForwards; animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) animation.removedOnCompletion = true layer.addAnimation(animation, forKey: "wobble") } func stopJiggling() { jiggling = false self.layer.removeAllAnimations() self.transform = CGAffineTransformIdentity self.layer.anchorPoint = CGPointMake(0.5, 0.5) } 
+3
Jul 28 '15 at 11:25
source share

The easiest way I know is to use Core Animation. Basically, you create a basic animation block, then perform a rotation transformation, and also set and repeat the counter. Core Animation then takes care of everything you need for this swing effect.

To start the Core Animation block, simply do:

 [UIView beginAnimations:@"any string as animationID" context:self]; [UIView setAnimationRepeatCount:10]; // rotate [UIView commitAnimations]; 

not verified. But there may be something you also have to do:

 [UIView setAnimationBeginsFromCurrentState:YES]; 

AFAIK setAnimationRepeatCount will have the effect that the animation is done, canceled, done, canceled, done, canceled, done ... as many times as you specify. Thus, you can first turn left without recounting, and then from this moment begin to oscillate with repetition. When this is done, you can turn back to identity transformation (= no rotation and scaling applied).

You can bind the animation by setting the animation delegate with

 [UIView setAnimationDelegate:self] 

and then

 [UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)]; 

and as soon as the animation stops, this method will be called. See the UIView class documentation for how to implement this method, which will be called when the animation stops. Basically, inside this method, you should perform the next step (for example, reverse rotation or something else), with a new animation block, but with the same context and animation identifier, and then (if necessary) specify a different didStopSelector file.

UPDATE:

You can check:

 [UIView setAnimationRepeatAutoreverses:YES]; 

it will oscillate back and forth automatically.

+2
May 30 '09 at 16:30
source share

You can create an endless wobble effect using CAKeyframeAnimation , for example:

 CGFloat degrees = 8.0; CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.duration = 0.6; animation.cumulative = YES; animation.repeatCount = 1; animation.values = @[@0.0, @RADIANS(-degrees) * 0.25, @0.0, @RADIANS(degrees) * 0.5, @0.0, @RADIANS(-degrees), @0.0, @RADIANS(degrees), @0.0, @RADIANS(-degrees) * 0.5, @0.0, @RADIANS(degrees) * 0.25, @0.0]; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; animation.removedOnCompletion = YES; [self.layer addAnimation:animation forKey:@"wobble"]; 
+2
Feb 14 '13 at 14:48
source share

Late party. I usually use spring with damping (iOS7 and newer). Quickly it looks like this:

  sender.transform = CGAffineTransformMakeScale(1.2, 1.2) UIView.animateWithDuration(0.30, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in sender.transform = CGAffineTransformMakeScale(1, 1) }) { (Bool) -> Void in //Do stuff when animation is finished } 

You can customize the effect by adjusting initialSpringVelocity and SpringWithDamping

+1
May 28 '16 at 13:23
source share

Based on EsbenB's answer, but updated for Swift 3 and for rotation:

  sender.transform = CGAffineTransform(rotationAngle: 12.0 * .pi / 180.0) UIView.animate(withDuration: 0.60, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: .curveEaseInOut, animations: { () -> Void in sender.transform = CGAffineTransform(rotationAngle: 0.0) }, completion: nil) 
+1
Apr 9 '17 at 10:06 on
source share

Well, the code provided by Ramin works well. But if you use the tab application and move on to the next tab element, then go back to the previous tab element, you will see that your view has been moved to the left, each time. So, the best practice is that you use the ViewWillAppear method as.

 - (void)viewWillAppear:(BOOL)animated { UIView* item = self.view; item.transform = CGAffineTransformIdentity; } 

therefore, each time you view the time, you will find the animation in the right place. Also use this method.

 [UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)]; 
0
Nov 20 '10 at 11:27
source share

Swift 3

 func startWiggling() { deleteButton.isHidden = false guard contentView.layer.animation(forKey: "wiggle") == nil else { return } guard contentView.layer.animation(forKey: "bounce") == nil else { return } let angle = 0.04 let wiggle = CAKeyframeAnimation(keyPath: "transform.rotation.z") wiggle.values = [-angle, angle] wiggle.autoreverses = true wiggle.duration = randomInterval(0.1, variance: 0.025) wiggle.repeatCount = Float.infinity contentView.layer.add(wiggle, forKey: "wiggle") let bounce = CAKeyframeAnimation(keyPath: "transform.translation.y") bounce.values = [4.0, 0.0] bounce.autoreverses = true bounce.duration = randomInterval(0.12, variance: 0.025) bounce.repeatCount = Float.infinity contentView.layer.add(bounce, forKey: "bounce") } func stopWiggling() { deleteButton.isHidden = true contentView.layer.removeAllAnimations() } 
0
Jul 30 '17 at 19:53 on
source share



All Articles