How to repeat UIView animation specific times - ios

I'd like to repeat this animation three times.
I confirmed this code worked without repeating.
[UIView animateWithDuration:1.0f
delay:0.1f
options:UIViewAnimationOptionCurveLinear
animations:^{
imageOne.center = CGPointMake(100, 150);
} completion:^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
}];
Would anyone tell me how to do?
Thanks!

int animationLoopCount = 0;
- (void)doAnimation
{
for (animationLoopCount; animationLoopCount < 3; animationLoopCount++) {
[UIView animateWithDuration:1.0f
delay:0.1f
options:UIViewAnimationOptionCurveLinear
animations:^{
imageOne.center = CGPointMake(100, 150);
} completion:^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
[self doAnimation];
if (animationLoopCount == 3) animationLoopCount = 0;
}];
}
}

There are a couple ways you could accomplish this, I think cleanest approach would probably be to use recursion.
Here is a general-purpose method animate things with animation blocks recursively.
- (void) animateWithDuration:(NSTimeInterval) duration
delay:(NSTimeInterval) delay
options:(UIViewAnimationOptions) options
animations:(void(^)( )) animations
completion:(void(^)( BOOL finished )) completion
repeatCount:(NSInteger) repeatCount {
// Only do the animation if we have an animations block, and our count is bigger than 0.
if ( repeatCount <= 0 || !animations )
return;
// Do the animation
[UIView animateWithDuration: duration
delay: delay
options: options
animations:^{
// Invoke the animations block
animations();
}
completion:^(BOOL finished) {
// Invoke the animation completion if it exists
if ( completion )
completion( finished );
// Invoke ourself again with a decremented repeat count.
[self animateWithDuration: duration
delay: delay
options: options
animations: animations
completion: completion
repeatCount: repeatCount - 1];
}];
}
Here is how you could use it in your code using the example you provided.
[self animateWithDuration: 1.0f
delay: 0.1f
options: UIViewAnimationOptionCurveLinear
animations: ^{
imageOne.center = CGPointMake(100, 150);
}
completion: ^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
}
repeatCount: 3];

The proper way to do this is like so:
[UIView animateWithDuration:1.0f
delay:0.1f
options:UIViewAnimationOptionCurveLinear
animations:^{
[UIView setAnimationRepeatCount:3.0];// <===
imageOne.center = CGPointMake(100, 150);
} completion:^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
}];

use this simple code
CABasicAnimation* animate = [CABasicAnimation animationWithKeyPath:#"position"];
[animate setDuration:1.0];
[animate setRepeatCount:3];
[animate setAutoreverses:YES];
[animate setFromValue:[NSValue valueWithCGPoint:
CGPointMake(imageOne.center.x, imageOne.center.y)]];
[animate setToValue:[NSValue valueWithCGPoint:
CGPointMake(imageOne.center.x, imageOne.center.y+100)]];
[line.layer addAnimation:animate forKey:#"position"];

I'd rather make something like this:
const CGFloat AnimationDuration = 1.0;
const CGFloat AnimationDelay = 0.1;
const NSUInteger AnimationCount = 3;
- (void) someAction
{
[self showAnimationTimes:AnimationCount];
}
- (void) showAnimationTimes:(NSUInteger)count
{
__block NSUInteger blockCount = count;
[self animation:^
{
imageOne.center = CGPointMake(100, 150);
}
completion:^(BOOL finished)
{
imageOne.center = CGPointMake(100, 200);
if (count > 1)
{
[self showAnimationTimes:--blockCount];
}
}];
}
- (void) animation:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:AnimationDuration
delay:AnimationDelay
options:UIViewAnimationOptionCurveLinear
animations:animations
completion:completion];
}
it's easier to understand for me.

Related

Animation works on iphone but not on ipad

I'm using this code to animate buttons to bounce with different delays:
- (void) animateWithDuration: (float) duration andObject: (UIButton *) buttonToAnimate{
buttonToAnimate.hidden = YES;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self animateButton: buttonToAnimate];
});
}
- (void) animateButtons
{
[self animateWithDuration:0.3 andObject:self.photoAlbum];
[self animateWithDuration:0.7 andObject:self.camera];
[self animateWithDuration: 0.45 andObject:self.helpInfo];
}
- (void) animateButton: (UIButton *) button
{
button.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
button.hidden = NO;
[UIView animateWithDuration:0.3/0.7 animations:^{
button.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3/1.0 animations:^{
button.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.9, 0.9);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3/1.0 animations:^{
button.transform = CGAffineTransformIdentity;
}];
}];
}];
}
It works perfectly on iphone, but on ipad (simulator) the buttons makes a little jump up to the left with about 20 points on the completion block. Then back 20 point on the second completion. Using option: UIViewAnimationOptionTransitionNone (or any of the options) on the animation lessens the jump a tiny bit but you can still see it. Any idea whats wrong?

How to autorepeat a set of chained UIView animations using UIViewAnimationOptionRepeat

I am trying to chain a set of UIView animateWithDuration: using the flag UIViewAnimationOptionRepeat
The animation steps I am trying to repeat are:
Animate a UIView 100 pixels to the left
Fade the UIView out (opacity '0')
While faded out (invisible), move it back to the original position (100 px to the right)
Fade the UIView back in (set opacity to '1')
Repeat the animation using flag UIViewAnimationOptionRepeat
I am not able to repeat the whole chain of animations - and only able to repeat the top most animation.
The code I have tried is this:
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat animations:^{
self.handImageView.frame = CGRectMoveByXPixels(self.handImageView.frame, -100);
[UIView animateWithDuration:0.5 delay:1 options:0 animations:^{
self.handImageView.alpha = 0;
} completion:^(BOOL finished) {
self.handImageView.frame = CGRectMoveByXPixels(self.handImageView.frame, 100);
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
self.handImageView.alpha = 1;
} completion:^(BOOL finished) {
}];
}];
} completion:^(BOOL finished) {
}];
The problem is that an animation that uses UIViewAnimationOptionRepeat never finishes and therefore never calls the completion block. However, you can repeat the animations using the performSelector method, as shown in the code below.
- (void)repeatingAnimationForView:(UIView *)view
{
[UIView animateWithDuration:1 delay:0 options:0
animations:^{ view.frame = CGRectMoveByXPixels(view.frame, -100); }
completion:^(BOOL finished) { if ( finished ) {
[UIView animateWithDuration:0.5 delay:0 options:0
animations:^{ view.alpha = 0; }
completion:^(BOOL finished) { if ( finished ) {
view.frame = CGRectMoveByXPixels(view.frame, 100);
[UIView animateWithDuration:0.5 delay:0 options:0
animations:^{ view.alpha = 1; }
completion:^(BOOL finished) { if ( finished ) {
if ( self.enableRepeatingAnimation )
[self performSelector:#selector(repeatingAnimationForView:) withObject:view afterDelay:0];
}}]; }}]; }}];
}
- (void)stopRepeatingAnimationForView:(UIView *)view
{
self.enableRepeatingAnimation = NO;
[view.layer removeAllAnimations];
view.frame = CGRectMake( 100, 137, 80, 50 );
view.alpha = 1;
}
- (void)startRepeatingAnimationForView:(UIView *)view
{
self.enableRepeatingAnimation = YES;
[self repeatingAnimationForView:view];
}
To stop the animations immediately, call the stopRepeatingAnimationForView method as shown above. To stop the animations at the end of a cycle, simply set self.enableRepeatingAnimation to NO.

UIView animation on completion not working properly

I have a problem using a slider to trigger a series of animations, here's the code:
-(void)slideAlpha:(id)sender{
self.bigPhotoViewA.alpha = self.alphaSlider.value;
if (self.alphaSlider.value == 1){
[UIView animateWithDuration:1
animations:^{
self.alphaSlider.alpha = 0;
} completion:nil
];
[self performSelector:#selector(nextPhotoAnimation) withObject:self afterDelay:5.0 ];
}
}
-(void) nextPhotoAnimation{
self.alphaSlider.value = 0;
[UIView animateWithDuration:2
animations:^{
self.bigPhotoViewA.alpha = 0.0;
self.bigPhotoView.alpha = 0.0;
self.smallPhotoView.center = CGPointMake(startX, startY);
}
completion:^(BOOL finished) {
NSLog(#"Animation ended");
self.smallPhotoView.image = ((UIImage *)[smallImagesArray objectAtIndex:imageCount]);
}
];
}
So, when the slider reaches a value of 1, nextPhotoAnimation is launched after a delay. So far so good. The problem comes inside nextPhotoAnimation. The animations block runs ok, but the completion block runs several times every time nextPhotoAnimation is called. I get the NSLog from 6 to 9 times displayed when nextPhotoAnimation starts, and then I get it again at the right time, after 2 seconds.
I've tried to replicate the problem with simpler code and the animation/completion flow works just fine.
Try this in nextPhotoAnimation,
-(void) nextPhotoAnimation{
self.alphaSlider.value = 0;
[UIView animateWithDuration:2
animations:^{
self.bigPhotoViewA.alpha = 0.0;
self.bigPhotoView.alpha = 0.0;
self.smallPhotoView.center = CGPointMake(startX, startY);
}
completion:^(BOOL finished) {
if (finished) {
NSLog(#"Animation ended");
self.smallPhotoView.image = ((UIImage *)[smallImagesArray objectAtIndex:imageCount]);
}
}
];
}
neither you are using
[UIView beginAnimations:nil context:nil];
nor
[UIView commitAnimations];
Best way is to create a UIView and then use the protocols.call your methods using [self YOURMETHOD]; in the View.
:-)

Animation of UIButton decreases its size

Here is a small clip of a view being animated by me:
Animation flash
I am having problems when animation from ? to S and vice versa. I am setting the appropriate frames to go off screen, and then back on from the other size. Why does the size of the black button decrease with every move around it?
-(void)moveToRight
{
//just made this method up, but my actual code
//special case
if (currentDay == 7) {
//move off the screen to the right
CGRect offScreenRightFrame = CGRectMake(self.circle.frame.origin.x + 60, self.circle.frame.origin.y, self.circle.frame.size.width, self.circle.frame.size.height);
//move to the left of the screen so we can animate it in
CGRect offScreenLeftFrame = CGRectMake(-40, self.circle.frame.origin.y, self.circle.frame.size.width, self.circle.frame.size.height);
if (self.delegate) {
if ([self.delegate respondsToSelector:#selector(dayChangedTo:)]) [self.delegate dayChangedTo:[self getCurrentDay]];
}
[self pulseCircle];
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.frame = offScreenRightFrame;
}completion:^(BOOL finished) {
if (finished) {
self.circle.alpha = 0.0f;
self.circle.frame = offScreenLeftFrame;
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.center = self.labelSun.center;
self.circle.alpha = 1.0f;
}completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.25f animations:^{
[self changeColors];
[self pulseCircle];
}];
}
}];
}
}];
}
-(void)moveLeft
{
if (currentDay == 8) {
CGRect circleFrame = self.circle.frame;
CGRect offScreenLeft = CGRectOffset(circleFrame, -20, 0);
CGRect offScreenRightFrame = CGRectMake(self.labelQuestion.frame.origin.x + 30, self.labelQuestion.frame.origin.y, circleFrame.size.width, circleFrame.size.height);
[self pulseCircle];
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.frame = frame;
}completion:^(BOOL finished) {
if (finished) {
self.circle.alpha = 0.0f;
self.circle.frame = frameFinal;
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.center = self.labelQuestion.center;
self.circle.alpha = 1.0f;
}completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.25f animations:^{
[self changeColors];
[self pulseCircle];
}];
}
}];
}
}];
}
- (void)pulseCircle
{
__block UILabel *day = [self getLabelOfDay];
[UIView animateWithDuration:TRANLATE_DURATION/2 delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.circle.layer.transform = CATransform3DMakeScale(1.35f, 1.35f, 1);
day.transform = CGAffineTransformMakeScale(1.35f, 1.35f);
}completion:^(BOOL finished) {
[UIView animateWithDuration:TRANLATE_DURATION/2 animations:^{
self.circle.layer.transform = CATransform3DIdentity;
day.transform = CGAffineTransformIdentity;
}];
}];
}
I just put my logic.. honestly i don't read/see your code/logic but may be my idea work for you ;)
So, I think.. you should add size of circle in public variable as default size. And set this size whenever you rich at.
if (currentDay == 0 || currentDay == 8)
{
// Here you need to set your default size.
}
May be my logic work for you, Because 1 to 7 day its work fine but issue created at when currentDay rich at 0 or 8 day.
Thanks :)

Slide UILabel Onto View Controller

I'm trying to move a UILabel from a starting position of off the top of my UIViewController in a smooth slide type animation so that it slides down from the top when the view loads and then stops at a y position for about 10 seconds. After 10 seconds I want to slide back off the view again.
-(void) animateInstructionLabel
{
float newX = 50.0f;
float newY = 100.0f;
[UIView transitionWithView:self.lblInstruction
duration:10.0f
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(newX, newY);
}
completion:^(BOOL finished) {
// Do nothing
}];
}
But I don't know how to do the 10 second delay and then move back within the method above. The end result is that I want this to be a label that appears like a notification and then moves off screen again.
Could someone hep me put these pieces described above together?
EDIT
I am adding this based on answer below:
-(void) animateInstructionLabel
{
float newX = lblInstruction.frame.origin.x;
float newY = 20.0f;
lblInstruction.center = CGPointMake(lblInstruction.frame.origin.x, -20.0f);
lblInstruction.bounds = CGRectMake(lblInstruction.frame.origin.x, -20.0f, 650.0f, 40.0f);
[UIView animateWithDuration:3.0f
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(newX, newY);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:5.0f
delay:5.0
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(newX, -20);
}
completion:^(BOOL finished) {
// Do nothing
}
];
}
];
}
But even though I am setting the label.center off the screen before the animation starts I am seeing that it moves from top left corner of the viewcontroller to the center of the viewcontroller. It is not keeping the x position that it should be set to before the animation begins.
Try adding this after your animation block (oldX,oldY are the old coordinates, before animating the label in):
double delayInSeconds = 10.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView transitionWithView:self.lblInstruction
duration:10.0f
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(oldX, oldY);
}
completion:^(BOOL finished) {
// Do nothing
}];
});
Something like this should work:
-(void) animateInstructionLabel {
float newX = 50.0f;
float newY = 100.0f;
labelInstruction.center = CGPointMake(newX, -20); // start off screen
[UIView animateWithDuration:10.0f
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(newX, newY);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:10.0f
delay:10.0
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(newX, -20);
}
completion:^(BOOL finished) {
// Do nothing
}
];
}
];
}
Here is what worked in case it helps somebody else. I had to use .frame instead of .center to set the initial position and then the subsequent animation back to that position.
-(void) animateInstructionLabel
{
lblInstruction.frame = CGRectMake(lblInstruction.frame.origin.x, -40.0f, 650.0f, 40.0f);
[self.view addSubview:lblInstruction];
lblInstruction.hidden = NO;
float newX = lblInstruction.frame.origin.x;
float newY = 20.0f;
[UIView animateWithDuration:3.0f
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.center = CGPointMake(newX, newY);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:3.0f
delay:5.0
options:UIViewAnimationCurveEaseInOut
animations:^(void) {
lblInstruction.frame = CGRectMake(lblInstruction.frame.origin.x, -40.0f, 650.0f, 40.0f);
}
completion:^(BOOL finished) {
lblInstruction.hidden = YES;
}
];
}
];
}
This smoothly makes the label slide down from off the view, stop for 5 seconds and then smoothly moves it back vertically off the view.
Thanks for the previous answers which lead me to the above final solution.

Resources