UIView animateKeyFramesWithDuration not starting at correct times - ios

I'm confused as to why this isn't working. I these three events to occur one after the other. First one zooms and Image out, second one fades in a background and third one animates the first image. But it actually occurs the other way round? Have I done something silly ?
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionBeginFromCurrentState animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.25 animations:^{
[v setFrame:CGRectMake(self.view.center.x - 125, self.view.center.y - 125, 250, 250)];
}];
[UIView addKeyframeWithRelativeStartTime:0.50 relativeDuration:0.75 animations:^{
[UIView transitionWithView:self.fullView
duration:0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:NULL
completion:NULL];
[self.fullView setHidden:NO];
}];
[UIView addKeyframeWithRelativeStartTime:1.50 relativeDuration:0.5 animations:^{
v.animationImages = [self.imageDict objectForKey:id];
v.animationRepeatCount = 1;
[v startAnimating];
}];
}
completion:nil];
EDIT//
Aside from the typo (1.50 for addKeyFrameWithRelativeStartTime), the third block wasn't working because the UIImageView startAnimating method needs to be placed in the completion block.

Your code looks suspicious. The last key frame animation that you have added has a relative start time of 1.5. What does that do ? It should be within the range of 0 to 1.
From apple documentation,
frameStartTime
The time at which to start the specified animations.
This value must be in the range 0 to 1, where 0 represents the start
of the overall animation and 1 represents the end of the overall
animation. For example, for an animation that is two seconds in
duration, specifying a start time of 0.5 causes the animations to
begin executing one second after the start of the overall animation.
frameDuration
The length of time over which to animate to the
specified value. This value must be in the range 0 to 1 and indicates
the amount of time relative to the overall animation length. If you
specify a value of 0, any properties you set in the animations block
update immediately at the specified start time. If you specify a
nonzero value, the properties animate over that amount of time. For
example, for an animation that is two seconds in duration, specifying
a duration of 0.5 results in an animation duration of one second.
I am quite sure that fixing the value to be within the range will do some good change. But, I am not sure if it fixes your issue as I dont understand your animation code.

Related

How can I animate UILabels from an array to move across the screen with an equal delay between them?

I have an array of UILabels that I want to animate across the screen. I am iterating though the array using a for loop like below:
for(int i=0; i<[self.onScreenLabels count]; i++)
{
UILabel *label = self.onScreenLabels[i];
int x = label.frame.origin.x;
int y = label.frame.origin.y;
label.center=CGPointMake(320, 0);
[self.view addSubview:label];
[UIView animateWithDuration:0.3 delay:1.0 options:0 animations:^{
label.center=CGPointMake(x, y);
} completion:^(BOOL finished){
}];
}
I want each UILabel to delay 1.0 second after the last before animating, e.g. wait 1 second, fire the first, wait 1 second fire the second, wait 1 second fire the third ....etc all the way to the end of the array. However using this code there is a 1 second delay but ALL the labels then animate at the same time. Why is this? Is there a way around it?
thanks
The delay is relative to the time at which you create the animation, so, because all of the animations are created at the same time in the loop they all have the same delay.
Instead, set the delay to the iteration index (i):
[UIView animateWithDuration:0.3 delay:i options:0 animations:^{ ...
so the first will start at 0, the second at 1, the third at 2, etc
(or (i + 1) if you prefer)
Use delay like this:
[UIView animateWithDuration:0.3 delay:i + 1.0 options:0 animations:^{
This will delay first label for 1 second and each later will be delayed for +1 second

Animation Blocks resets to original position after updating text

I'm currently testing my apps for the release of IOS 8. I noticed that after I performed an animation block, the animation resets if I update the text of any label. I ran a simple example with one method shown below. Running this example results in the following:
Clicking myButton the first time- animation runs but resets when the label text is changed.
Clicking myButton the second time - animation runs but does not reset to original position.
It seems like this happens because the label text doesn't change. If I completely remove the line updating the text, this also stops the animation from resetting at the end.
I would like to fix this so that when the method runs, the label text can be updated without resetting the animation.
- (IBAction)move:(id)sender {
[UIView animateWithDuration:0.4 delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.myButton.center = CGPointMake(200, 300);
}completion:^(BOOL finished){
if(finished){
self.myLabel.text=#"moved";
}
}];
}
This problem can be caused by having Auto Layout set on the UIView. Strictly speaking, if you're using Auto Layout, then you shouldn't animate the absolute position of objects -- you should animate their constraints instead.
Changing the label text once your animation is underway triggers a layout refresh, and iOS shuffles everything around to comply with the original view constraints. (I suspect this is a behavioural change from iOS7).
Quick fix: un-check Auto Layout on the View, and this should work as expected.
Try this. Put the desired animation in the finish block also.
- (IBAction)move:(id)sender {
[UIView animateWithDuration:0.4 delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.myButton.center = CGPointMake(200, 300);
}completion:^(BOOL finished){
if(finished){
self.myLabel.text=#"moved";
self.myButton.center = CGPointMake(200, 300);
}
}];
}

UIView to make an arm wave

I'm trying to make an arm wave effect (Hello!) with UIView animations, but it snaps back to the beginning when the last one goes off. I want the wave to go back and forth.
First keyframe: Rotation 30˚
Second keyframe: Rotation -30˚
Third keyframe: SHOULD BE Rotation 0˚
arm.layer.anchorPoint = CGPointMake(1.0, 1.0);
float x = arm.frame.origin.x + arm.frame.size.width;
float y = arm.frame.origin.y + arm.frame.size.height;
arm.layer.frame = CGRectMake(x, y, arm.frame.size.width, arm.frame.size.height);
[self.scrollView arm];
float degrees = 30.0;
float radians = (degrees/90.0)*M_PI;
[UIView animateKeyframesWithDuration:4.0f delay:0.0 options:UIViewKeyframeAnimationOptionRepeat
animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:.5 animations:^{
arm.transform = CGAffineTransformMakeRotation(radians);
}];
[UIView addKeyframeWithRelativeStartTime:.5 relativeDuration:.75 animations:^{
arm.transform = CGAffineTransformMakeRotation(-radians);
}];
[UIView addKeyframeWithRelativeStartTime:1.25 relativeDuration:.5 animations:^{
arm.transform = CGAffineTransformMakeRotation(0);
}];
The reason is that you used up the whole animation on the first two frames of the keyframe animation. These are relative times and relative durations; they must all be less than 1!
So, on the first one you use up half the animation (.5).
On the second one you start halfway through the animation (.5) and then you use up the entire rest of the animation (.75 is larger than .5 which is all you've got left).
So the third one never happens. And in any case it's completely nuts: you can't have a relative start time of 1.75 because 1 is the end of the animation. Read the docs:
This value must be in the range 0 to 1, where 0 represents the start of the overall animation and 1 represents the end of the overall animation. For example, for an animation that is two seconds in duration, specifying a start time of 0.5 causes the animations to begin executing one second after the start of the overall animation.

Smooth frame animating on UIView

I am trying to move a UIView around the screen by incrementing the UIView's x property in an animation block. I want the element to move continuously so I cannot just specify an ending x and up the duration.
This code works but it is very choppy. Looks great in the simulator but choppy on the device.
-(void)moveGreyDocumentRight:(UIImageView*)greyFolderView
{
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
NSInteger newX = greyFolderView.frame.origin.x + 5.0;
greyFolderView.frame = CGRectMake(newX, greyFolderView.frame.origin.y, greyFolderView.frame.size.width, greyFolderView.frame.size.height);
}
} completion:^(BOOL finished) {
[self moveGreyDocumentRight:greyFolderView];
}];
}
You're fighting the view animation here. Each one of your animations includes a UIViewAnimationOptionCurveEaseInOut timing curve. That means that every 0.05 seconds you try to ramp up your speed then slow down your speed then change to somewhere else.
The first and simplest solution is likely to change to a linear timing by passing the option UIViewAnimationOptionCurveLinear.
That said, making a new animation every 5ms really fights the point of Core Animation, complicating the code and hurting performance. Send the frame it to the place you currently want it to go. Whenever you want it to go somewhere else (even if it's still animating), send it to the new place passing the option UIViewAnimationOptionBeginFromCurrentState. It will automatically adjust to the new target. If you want it to repeat the animation or bounce back and forth, use the repeating options (UIViewAnimationOptionRepeat and UIViewAnimationOptionAutoreverse).

Unexpected behaviour with UIViewAnimationOptionBeginFromCurrentState

I'm trying to implement multistage animation using UIViewAnimationOptionBeginFromCurrentState to allow the user to cancel the animation at will. The animation is a view that continually and cyclically animates between two sizes. When the user touches the view to cancel the animation, I want the view to quickly revert back to its original, small size, whether it was growing or shrinking at the time.
I'm implementing the multistaging aspect by having two separate animations, one for growing the view and one for shrinking it. Each calls the other routine in its completion block, thus cycling forever unless the abort flag has been set.
I get the expected behaviour if abort is called during the grow-the-view animation: the animation quickly and immediately returns the view to its original, small size and stops. Good!
However, if abort is called during the shrink-the-view animation cycle, the view continues to shrink at the same speed (and then stops as expected), as if the UIViewAnimationOptionBeginFromCurrentState option was never invoked.
Code will hopefully make this clearer and hopefully somebody can see what I can't.
- (void)stopAnimating {
abort = YES;
[UIView animateWithDuration:.2 // some small interval
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ self.frame = minRect;}
completion:^(BOOL done){}
];
}
- (void)animateSmall {
[UIView animateWithDuration:4
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{self.frame = minRect;}
completion:^(BOOL done){if (!abort)[self animateBig];}
];
}
- (void)animateBig {
[UIView animateWithDuration:4
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{self.frame = maxRect;}
completion:^(BOOL done){if (!abort)[self animateSmall];}
];
}
Just a guess here, because your code looks exactly as I would have done it. But I think what's going on is that the abort animation is setting the same attribute to the same value as the animation it's interrupting, and this gets treated as equivalent and not in need of change (even though the duration changes).
A test of this theory - and a fix to the problem - would be to make your oscillating minRect just a little bit different in size than your steady state minRect.
Hope this works. Good luck.

Resources