I would like to present a view in the same manner as that of UIAlertView - a pop/spring. Unfortunately subclassing UIAlertView is not an option for the view I need to present. I have written some code, but I can't seem to get it as realistic as I would like. I would appreciate any suggestions for greater realism or a link if anything similar has been done (I could not find anything on Google). Thank you.
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = [UIColor whiteColor];
v = [[UIView alloc] initWithFrame:CGRectMake(140, 140, 60, 60)];
v.backgroundColor = [UIColor blueColor];
[self addSubview:v];
[self animate];
}
return self;
}
- (void)animate {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.2];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(popStep1Complete)];
v.frame = CGRectMake(90, 90, 140, 140);
[UIView commitAnimations];
}
- (void)popStep1Complete {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.15];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(popStep2Complete)];
v.frame = CGRectMake(110, 110, 100, 100);
[UIView commitAnimations];
}
- (void)popStep2Complete {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.15];
v.frame = CGRectMake(100, 100, 120, 120);
[UIView commitAnimations];
}
- (void) attachPopUpAnimation
{
CAKeyframeAnimation *animation = [CAKeyframeAnimation
animationWithKeyPath:#"transform"];
CATransform3D scale1 = CATransform3DMakeScale(0.5, 0.5, 1);
CATransform3D scale2 = CATransform3DMakeScale(1.2, 1.2, 1);
CATransform3D scale3 = CATransform3DMakeScale(0.9, 0.9, 1);
CATransform3D scale4 = CATransform3DMakeScale(1.0, 1.0, 1);
NSArray *frameValues = [NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:scale1],
[NSValue valueWithCATransform3D:scale2],
[NSValue valueWithCATransform3D:scale3],
[NSValue valueWithCATransform3D:scale4],
nil];
[animation setValues:frameValues];
NSArray *frameTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.5],
[NSNumber numberWithFloat:0.9],
[NSNumber numberWithFloat:1.0],
nil];
[animation setKeyTimes:frameTimes];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.duration = .2;
[self.layer addAnimation:animation forKey:#"popup"];
}
the pointer to delackner's post is the best initial one i found as well. i would only offer for folks trying to really mimic UIAlertView a few things:
adding the rounded edges, border, and semi-transparent layer color
some alpha fade-in/-out, and
redoing with blocks as somewhat more succinct with latest iOS toolchain
i also found the initial 1.1 scale-out he suggests too large, 1.05 seemed more correct visually to me in most cases
Code:
self.layer.cornerRadius = 10.0;
[self.layer setMasksToBounds:YES];
self.layer.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.60].CGColor;
self.layer.borderColor = [UIColor whiteColor].CGColor;
self.layer.borderWidth = 1.1;
if (self.hidden == YES) { // swoop in if coming from hidden, otherwise pulse in-place
self.transform = CGAffineTransformMakeScale(0.6, 0.6);
}
self.hidden = NO;
[UIView animateWithDuration:0.2
animations:^{
self.transform = CGAffineTransformMakeScale(1.05, 1.05);
self.alpha = 0.8;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1/15.0
animations:^{
self.transform = CGAffineTransformMakeScale(0.9, 0.9);
self.alpha = 0.9;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:1/7.5
animations:^{
self.transform = CGAffineTransformIdentity;
self.alpha = 1.0;
}
];
}
];
}
];
One thing: multi-step animations like this are much easier if you use a CAKeyframeAnimation instead of mutiple UIView queued animations.
Related
I have two UIImageView - leftSide and rightSide, which I would like to fade in and then fade out, one after the other, in a loop.
How do I do this in Xcode? I tried CABasicAnimation but this did not work (I'm not sure why, no errors at all, the images do not fade).
Thanks!
EDIT: This is the code I was using:
-(void)leftSideFade{
CABasicAnimation *blink;
blink = [CABasicAnimation animationWithKeyPath:#"opacity"];
blink.duration = 1.0;
blink.repeatCount = 5;
blink.autoreverses = YES;
blink.fromValue = [NSNumber numberWithFloat:1.0];
blink.toValue = [NSNumber numberWithFloat:0.0];
[leftSideStart.layer addAnimation:blink forKey:#"animateOpacity"];
[self rightSideFade];
}
-(void)rightSideFade{
CABasicAnimation *blink;
blink = [CABasicAnimation animationWithKeyPath:#"opacity"];
blink.duration = 1.0;
blink.repeatCount = 5;
blink.autoreverses = YES;
blink.fromValue = [NSNumber numberWithFloat:1.0];
blink.toValue = [NSNumber numberWithFloat:0.0];
[rightSideStart.layer addAnimation:blink forKey:#"animateOpacity"];
[self leftSideFade];
}
- (void)fadeIn
{
[UIView beginAnimations:#"fadeIn Animation" context:nil];
[UIView setAnimationDuration:1.0];
imageView.alpha = 1.0;
[UIView commitAnimations];
}
Try this to.
Try using the following :
- (void) fadeinRightFadwOutLeft {
[UIView beginAnimations:#"start" context:nil];
[UIView setAnimationDuration:1.0];
rightSide.alpha = 0.0;
leftSide.alpha = 1.0;
[UIView commitAnimations];
}
- (void) fadeinLeftFadwOutRight {
[UIView beginAnimations:#"start" context:nil];
[UIView setAnimationDuration:1.0];
leftSide.alpha = 0.0;
rightSide.alpha = 1.0;
[UIView commitAnimations];
}
How about a simpler way with UIView animateWithDuration:animations:completion: ?
There you just change alpha of your views and they will fade in/out
Here is a sample code:
[UIView animateWithDuration:duration
animations:^
{
//Here you set values you want to see in the end of animation
leftView.alpha = 0.0f;
rightView.alpha = 1.0f;
}
completion:^(BOOL finished)
{
//something to do after animation finished
}
I try to implement the animation:
when you enter iPhone Gallery, press the image, you see full-screen image. Below you can see toolbar with trash button. When you press this button, the image is being deleted with animation.
I try to implement this, but I don't know, how to implement the transform of image, apple use.
This is the best, I could do:
[UIView transitionWithView:self.view duration:0.1 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[self.view addSubview:scrollImageView];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = scrollImageView.frame;
frame.size = CGSizeMake(frame.size.width * 0.75, frame.size.height * 0.75);
frame.origin = CGPointMake((size.width - frame.size.width) / 2, (size.height - frame.size.height) / 2);
scrollImageView.frame = frame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = scrollImageView.frame;
frame.size = CGSizeMake(frame.size.width * 0.05, frame.size.height * 0.05);
frame.origin = CGPointMake(size.width, size.height);
scrollImageView.frame = frame;
CGAffineTransform transform = scrollImageView.transform;
CGAffineTransform rotatedTransform = CGAffineTransformRotate(transform, 45 * 3.14 / 180);
scrollImageView.transform = rotatedTransform;
} completion:^(BOOL finished) {
[scrollImageView removeFromSuperview];
}];
}];
}];
Thank you in advance.
Update
As I understand, I can't do this animation with Core-Animation, but may anyone can advice me the animation the most simular to iPhone Gallery animation, but without using OpenGL?
You can use following example for this animation:
UIView *senderView = (UIView*)sender;
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];
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:icon.center];
[movePath addQuadCurveToPoint:senderView.center
controlPoint:CGPointMake(senderView.center.x, icon.center.y)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = YES;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
opacityAnim.removedOnCompletion = YES;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
animGroup.duration = 0.5;
[icon.layer addAnimation:animGroup forKey:nil];
I have modified the code, you have to perform following changes in it, set the sender view as self.view, and change the ending point of animation (which is currently senderView.center) according to your requirement
I know its a bit late. But, you should check Ciechan's solution named BCGenieEffect. Can be found here. Its pure Core Animation and very easy to understand. I think that's what you are looking for.
Good luck
At this point the exact animation you are talking about cannot be done using Core Animation or UIKit. You would need to use OpenGL and apply the image as a texture and do your animation in there.
I think you are talking about the "suck" transition animation.
It is possible to trigger it, but it is a private transition, and Apple may reject your app if you use it.
This code should do it, using a transition code of 103:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition: transtionIndex forView:containerView cache:NO];
[containerView addSubview: newView];
[oldView removeFromSuperview];
[UIView commitAnimations];
Or search the net for a Core Image transition called "suckEffect"
I have an UIImageView that I want to fade in.
Is there a way to enable that on an underlying UIViewController?
I have translated the simplest answer, though they all work, C# .NET for the MonoTouch users:
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
UIView.BeginAnimations ("fade in");
UIView.SetAnimationDuration (1);
imageView.Alpha = 1;
UIView.CommitAnimations ();
}
Initially set the alpha of your imageview as 0 as imageView.alpha = 0;
- (void)fadeInImage
{
[UIView beginAnimations:#"fade in" context:nil];
[UIView setAnimationDuration:1.0];
imageView.alpha = 1.0;
[UIView commitAnimations];
}
Change the animation duration to what ever length you want.
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myImage.png"]];
myImageView.center = CGPointMake(100, 100);
myImageView.alpha = 0.0;
[self.view addSubview:myImageView];
[UIView animateWithDuration:5.0 animations:^{
myImageView.alpha = 1.0;
}];
this simple code:
[UIView animateWithDuration:5.0 animations:^{
theImage.alpha = 1.0;
}];
the UIImage is in the view and is connected via an IBOutlet
you can also, set the alpha from the utility panel.
Use below in your UIViewController
// add the image view
[self.view addSubview:myImageView];
// set up a transition animation
CATransition *animate = [CATransition animation];
[animate setDuration:self.animationDelay];
[animate setType:kCATransitionPush];
[animate setSubtype:kCATransitionFade];
[animate setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[[self layer] addAnimation:animate forKey:#"fade in"];
I would use UIView's +transitionWithView:duration:options:animations:completion:
It is very efficient and powerful.
[UIView transitionWithView:imgView duration:1 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
imgView.image = [UIImage imageNamed:#"MyImage"];
} completion:nil];
You can use this code:
Fade In Animation:
self.yourComponent.alpha = 0.0f;
[UIView beginAnimations:#"fadeIn" context:nil];
[UIView setAnimationDuration:1.0]; // Time in seconds
self.yourComponent.alpha = 1.0f;
Fade Out Animation:
self.yourComponent.alpha = 1.0f;
[UIView beginAnimations:#"fadeOut" context:nil];
[UIView setAnimationDuration:1.0]; // Time in seconds
self.yourComponent.alpha = 0.0f;
self.yourComponent can be a UIView, UIImageView, UIButton or any other component.
Swift version
func fadeIn(){
UIView.beginAnimations("fade in", context: nil);
UIView.setAnimationDuration(1.0);
imageView.alpha = 1.0;
UIView.commitAnimations();
}
Given:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDuration:.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:card cache:NO];
myPic = [UIImage UIImagenamed: #"mySecondImage.png"];
[UIView commitAnimations];[/CODE]
Which animates 'myPic' right to left with a flip.
I need to get the same animation, but vertically. Flip from Top or Flip from Bottom. I looked around, no one really had a working model suggested.
I tried this, yet, no luck:
float duration = .5;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.x"];
animation.fromValue = [NSNumber numberWithDouble:0.0f * M_PI];
animation.toValue = [NSNumber numberWithDouble:1.0f * M_PI];
animation.duration = duration;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeBoth;
animation.repeatCount =1;;
animation.beginTime = CACurrentMediaTime();
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
card.layer.anchorPoint = CGPointMake(0.5, 1.0);
[card.layer addAnimation:animation forKey:#"rotationX"];[/CODE]
Any input?
Thanks in advance.
I've also needed flip from bottom animation. I've compiled couple solutions and this works for me
- (CATransform3D) makeRotationAndPerspectiveTransform:(CGFloat) angle {
CATransform3D transform = CATransform3DMakeRotation(angle, 1.0f, 0.0f, 0.0f);
transform.m34 = 1.0 / -500;
return transform;
}
- (void) flipFromBottom {
//setup firstView to be visible
view1.layer.transform = CATransform3DMakeRotation(0, 1.0f, 0.0f, 0.0f);
view1.hidden = NO;
// setup secondView to be partialy rotated and invisible
view2.layer.transform = [self makeRotationAndPerspectiveTransform:M_PI/2];
view2.hidden = YES;
// making sure that both views have the same position
view2.frame = view1.frame;
CFTimeInterval duration = 2.0;
[UIView animateWithDuration:duration/2
delay:0
options:UIViewAnimationCurveEaseIn
animations:^{
view1.layer.transform = [self makeRotationAndPerspectiveTransform:-M_PI / 2];
}
completion:^(BOOL finished){
view1.hidden = YES;
view2.hidden = NO;
[UIView animateWithDuration:duration /2
delay:0
options:UIViewAnimationCurveEaseOut
animations:^{
view2.layer.transform = CATransform3DMakeRotation(0.0f, 1.0f, 0.0f, 0.0f);
}
completion:NULL];
}];
}
On login failure, I'd prefer to avoid showing an alert, it's too fleeting. Showing the alert and then showing the text somewhere on the login screen seems like duplication.
So I'd like for it to graphically shake my login view when the user enters the wrong user ID and password like the Mac login screen does.
Anyone know if there's a way to pull this off, or have any suggestions for another effect I could use?
I think this is a more efficient solution:
Swift:
let anim = CAKeyframeAnimation( keyPath:"transform" )
anim.values = [
NSValue( CATransform3D:CATransform3DMakeTranslation(-5, 0, 0 ) ),
NSValue( CATransform3D:CATransform3DMakeTranslation( 5, 0, 0 ) )
]
anim.autoreverses = true
anim.repeatCount = 2
anim.duration = 7/100
viewToShake.layer.addAnimation( anim, forKey:nil )
Obj-C:
CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:#"transform" ] ;
anim.values = #[
[ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-5.0f, 0.0f, 0.0f) ],
[ NSValue valueWithCATransform3D:CATransform3DMakeTranslation( 5.0f, 0.0f, 0.0f) ]
] ;
anim.autoreverses = YES ;
anim.repeatCount = 2.0f ;
anim.duration = 0.07f ;
[ viewToShake.layer addAnimation:anim forKey:nil ] ;
Only one animation object is created and it's all performed at the CoreAnimation level.
Using iOS 4+ block based UIKit animations (and loosely based on on jayccrown's answer):
- (void)shakeView:(UIView *)viewToShake
{
CGFloat t = 2.0;
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, 0.0);
CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, 0.0);
viewToShake.transform = translateLeft;
[UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount:2.0];
viewToShake.transform = translateRight;
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
viewToShake.transform = CGAffineTransformIdentity;
} completion:NULL];
}
}];
}
I had seen some wobble animation and changed it to shake a view t pixels upright and downleft:
- (void)earthquake:(UIView*)itemView
{
CGFloat t = 2.0;
CGAffineTransform leftQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, t, -t);
CGAffineTransform rightQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, t);
itemView.transform = leftQuake; // starting point
[UIView beginAnimations:#"earthquake" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:5];
[UIView setAnimationDuration:0.07];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(earthquakeEnded:finished:context:)];
itemView.transform = rightQuake; // end here & auto-reverse
[UIView commitAnimations];
}
- (void)earthquakeEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
if ([finished boolValue])
{
UIView* item = (UIView *)context;
item.transform = CGAffineTransformIdentity;
}
}
Here's a tutorial that details how to do it in Cocoa. Should be the same for the iPhone (or at least quite similar).
http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/
Simply changing the X coordinate of the center property of your view might do the trick. If you haven't done any core animation before it's pretty straight-forward.
First, start an animation right, then listen for it to finish, and then move back to the left, and so on. Getting the timing down so it "feels right" might take a while.
- (void)animationFinishCallback:(NSString *)animationID finished:(BOOL)finished context:(void *)context
{
if ([animationID isEqualToString:#"MoveRight"]) {
[UIView beginAnimations:#"MoveLeft" context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay: UIViewAnimationCurveEaseIn];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationFinishCallback:finished:context:)];
myView.center = CGRectMake(newX, newY);
[UIView commitAnimations];
}
}
This UIView category snippet worked for me. It's using 3 CABasingAnimations applied to view's layer.
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#define Y_OFFSET 2.0f
#define X_OFFSET 2.0f
#define ANGLE_OFFSET (M_PI_4*0.1f)
#interface UIView (shakeAnimation)
-(BOOL)isShakeAnimationRunning;
-(void)startShakeAnimation;
-(void)stopShakeAnimation;
#end
#implementation UIView (shakeAnimation)
-(BOOL)isShakeAnimationRunning{
return [self.layer animationForKey:#"shake_rotation"] != nil;
}
-(void)startShakeAnimation{
CFTimeInterval offset=(double)arc4random()/(double)RAND_MAX;
self.transform=CGAffineTransformRotate(self.transform, -ANGLE_OFFSET*0.5);
self.transform=CGAffineTransformTranslate(self.transform, -X_OFFSET*0.5f, -Y_OFFSET*0.5f);
CABasicAnimation *tAnim=[CABasicAnimation animationWithKeyPath:#"position.x"];
tAnim.repeatCount=HUGE_VALF;
tAnim.byValue=[NSNumber numberWithFloat:X_OFFSET];
tAnim.duration=0.07f;
tAnim.autoreverses=YES;
tAnim.timeOffset=offset;
[self.layer addAnimation:tAnim forKey:#"shake_translation_x"];
CABasicAnimation *tyAnim=[CABasicAnimation animationWithKeyPath:#"position.y"];
tyAnim.repeatCount=HUGE_VALF;
tyAnim.byValue=[NSNumber numberWithFloat:Y_OFFSET];
tyAnim.duration=0.06f;
tyAnim.autoreverses=YES;
tyAnim.timeOffset=offset;
[self.layer addAnimation:tyAnim forKey:#"shake_translation_y"];
CABasicAnimation *rAnim=[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rAnim.repeatCount=HUGE_VALF;
rAnim.byValue=[NSNumber numberWithFloat:ANGLE_OFFSET];
rAnim.duration=0.15f;
rAnim.autoreverses=YES;
rAnim.timeOffset=offset;
[self.layer addAnimation:rAnim forKey:#"shake_rotation"];
}
-(void)stopShakeAnimation{
[self.layer removeAnimationForKey:#"shake_translation_x"];
[self.layer removeAnimationForKey:#"shake_translation_y"];
[self.layer removeAnimationForKey:#"shake_rotation"];
[UIView animateWithDuration:0.2f animations:^{
self.transform=CGAffineTransformRotate(self.transform, ANGLE_OFFSET*0.5);
self.transform=CGAffineTransformTranslate(self.transform, X_OFFSET*0.5, Y_OFFSET*0.5f);
}];
}
#end
Hope it helpes someone :)
In iOS 7.0 or later, UIKit keyframe animation is available.
[UIView animateKeyframesWithDuration:0.5 delay:0.0 options:0 animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
NSInteger repeatCount = 8;
NSTimeInterval duration = 1.0 / (NSTimeInterval)repeatCount;
for (NSInteger i = 0; i < repeatCount; i++) {
[UIView addKeyframeWithRelativeStartTime:i * duration relativeDuration:duration animations:^{
CGFloat dx = 5.0;
if (i == repeatCount - 1) {
viewToShake.transform = CGAffineTransformIdentity;
} else if (i % 2) {
viewToShake.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, -dx, 0.0);
} else {
viewToShake.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, +dx, 0.0);
}
}];
}
} completion:completion];
very easy shake categorie for UIVoew
https://github.com/jonasschnelli/UIView-I7ShakeAnimation
I know the question is already answered, but since I have already implemented something like this previously, I feel it can't hurt to add it:
CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"];
NSArray *transformValues = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:((M_PI)/64)],
[NSNumber numberWithFloat:(-((M_PI)/64))],
[NSNumber numberWithFloat:((M_PI)/64)],
[NSNumber numberWithFloat:(-((M_PI)/64))],
[NSNumber numberWithFloat:((M_PI)/64)],
[NSNumber numberWithFloat:(-((M_PI)/64))],
[NSNumber numberWithFloat:0],
nil];
[shakeAnimation setValues:transformValues];
NSArray *times = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.14f],
[NSNumber numberWithFloat:0.28f],
[NSNumber numberWithFloat:0.42f],
[NSNumber numberWithFloat:0.57f],
[NSNumber numberWithFloat:0.71f],
[NSNumber numberWithFloat:0.85f],
[NSNumber numberWithFloat:1.0f],
nil];
[shakeAnimation setKeyTimes:times];
shakeAnimation.fillMode = kCAFillModeForwards;
shakeAnimation.removedOnCompletion = NO;
shakeAnimation.duration = 0.6f;
[self.viewToShake.layer addAnimation:shakeAnimation forKey:#"anim"];
Also, since you want the shaking to indicate that the user failed to log in, you might also consider adding this animation that tints the screen red while the screen shakes:
//Put this in the header (.h)
#property (nonatomic, strong) UIView *redView;
//Put this in the implementation (.m)
#synthesize redView;
//Put this in viewDidLoad
self.redView = [[UIView alloc] initWithFrame:self.view.frame];
self.redView.layer.opacity = 0.0f;
self.redView.layer.backgroundColor = [[UIColor redColor] CGColor];
//Put this wherever you check if the login failed
CAKeyframeAnimation *redTint = [CAKeyframeAnimation animationWithKeyPath:#"opacity"];
NSArray *transformValues = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.2f],
[NSNumber numberWithFloat:0.0f],
nil];
[redTint setValues:transformValues];
NSArray *times = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.5f],
[NSNumber numberWithFloat:1.0f],
nil];
[redTint setKeyTimes:times];
redTint.fillMode = kCAFillModeForwards;
redTint.removedOnCompletion = NO;
redTint.duration = 0.6f;
[self.redView.layer addAnimation:shakeAnimation forKey:#"anim"];
Hope this helps!
Using Auto Layout, I adapted Chris Miles' answer but animated NSLayoutConstraints like this:
NSLayoutConstraint *left = ...
NSLayoutConstraint *right = ...
[UIView animateWithDuration:0.08 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount:3];
left.constant = 15.0;
right.constant = 25.0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.08 animations:^{
left.constant = 20.0;
right.constant = 20.0;
[self.view layoutIfNeeded];
} completion:NULL];
}
}];
A solution I used for constraints which I set in my storyboard. Without using animateWithDuration.
#IBOutlet var balloonHorizontalConstraint: NSLayoutConstraint!
NSTimer.scheduledTimerWithTimeInterval(0.04, target: self, selector: "animateBalloon", userInfo: nil, repeats: true)
func animateBalloon() {
switch balloonHorizontalConstraint.constant {
case -46:
balloonHorizontalConstraint.constant = -50
default:
balloonHorizontalConstraint.constant = -46
}
}
In my case the animation just kept on going, but I pop my viewcontroller after a duration of a few seconds, this stops my timer aswell.