smoothly stopping a core animation rotation - iphone

How do you stop the rotation of a custom drawn CALayer on the iPhone?
This
CATransform3D pausetransform = ((CALayer *)[layer presentationLayer]).transform;
[layer removeAllAnimations];
layer.transform = pausetransform;
stops the animation, then immediately rotates the layer back a little bit. Probably because after the pausetransform value is set the animation continues for a short while.
Setting the layer.transform value in the animation's - (void) animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)finishednormally delegate method briefly displays the layer in the starting position of the animation before it rotates to the correct position.

It turns out the UIActionSheet I was setting up when calling [layer removeAllAnimations] took so long to display it was postponing the actual stopping of the animation. By moving the setup of the UIActionSheet to the - (void) animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)finishednormally delegate method the rotation now stops smoothly.

Another way is to call
[view.layer removeAnimationForKey:#"name of your animation"];
However, in both removes, depending on how you set up the animation, the animation stops at the next iteration of the animation. If, let's say, you were spinning in 10 degree increments, the animation would stop before the next 10 degree increment.

Related

Problems with UIView animation

I am performing animation on my view. But I am facing few problems with that. I have to button's startAnimation and Stop Animation. For performing animation on my imageview I am using following code in startAnimation button's action:
UIViewAnimationOptions option = UIViewAnimationOptionRepeat + UIViewAnimationOptionAutoreverse;
[UIView animateWithDuration:2.5 delay:0.0 options:option
animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(2.0, 2.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(-50.0, -100.0);
CGAffineTransform mix = CGAffineTransformConcat(scale, translate);
imv.transform = mix;
}
completion:^(BOOL finished) {
}
];
This is working.
Now the problems I am facing are:
How to stop this animation on my stopAnimation button's action?
If ImageView is animation and I enter in background after that I again come in foreground ImageView sticks there and do not animates.
How to solve these issues?
On your stopAnimation button you would need to remove animations from the UIView's layer. First you must import Quartzcore.framework.
#import <QuartzCore/QuartzCore.h>
[yourView.layer removeAllAnimations];
As for your other issue of pausing your animation while going into the background and coming back to foreground, theres no way to 'pause' an animation. Your best bet would be to manually handle that situation by restarting your animation.
To stop the animation you can use
#import <QuartzCore/QuartzCore.h>
...
[imv.layer removeAllAnimations];
As for making an animation restart it is a complex problem and the answer depends on what you want the application to do when it restores. One possible answer would be to keep track of where you are animating the ImageView to and then in ApplicationDidEnterBackground you could cancel all the animations and simply move it there. Then when you restored it would be in the correct location.
If you actually want to continue the animations I would have a read of some other Stack Overflow answers and see if that helps.
Restoring animation where it left off when app resumes from background
iOS, Restarting animation when coming out of the background
For further reference, for the second part of your response you have to restore the matrix to the identity before doing anything:
imv.transform = CGAffineTransformIdentity;

How to obtain changes to view position when animating frame?

I need to update a view continuously depending on another view's position while it's moving across the screen. I tried using KVO on frame, but it seems to trigger only at the beginning of the animation. Is there a recommended way of doing this?
You want to have a look at the Core Animation presentation layer which should tell you the position during the animation.
I would add a repeatable timer and make something like:
[self updateView:_view
usingPosition:[_anotherView.presentationLayer position]];
(Don't you know _anotherView's animation in advance? Maybe then you can setup _view animation directly?)
Why not just set both animations for both view at the same time?
[UIImageView animateWithDuration:aDuration
delay:0
options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState
animations:^(void) {
view1.center = somePosition;
view2.center = someOtherPosition;
} completion:NULL];
They will move at the same time and with the option : UIViewAnimationOptionBeginFromCurrentState if you start an other animation before this one is finished the views will start at the point they were stopped.
But if it's not what you are looking for, can you update your question with precision on how view1 is moving.

Is it possible to change UIView's "center" property inside the overriden touchesEnded method and redraw the view?

I want to implement a simple animation based on UIView's center property. I have a simple view and I can drag it (UIView touchesMoved is overriden). The animation should fade slowly like the view is moved under it's own inertia for some time after the user releases it. But for now I want simply to move the view after touch ends. Here is the code I have in touchesEnded:
int i;
for (i=1;i<4;i++)
{
self.center = CGPointMake(10*i, 12*i);
[self setNeedsDisplay];
usleep(100000);
}
The problem is when I run this, the code is executed nicely but "UIView" changes to late. I changed usleep time and other parameters but the result is the same. It looks like all the "pending changes" in the view are performed only after the overriden touchesEnded is finished.
Is this the right way of implementing such user interface feature or should I look for some other approaches?
Thanks in advance.
If you know the final positions you want your view center to have you could do this with an animation:
[UIView animateWithDuration:20
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
youView.center = CGPointMake(newCenterX, newCenterY);
}
completion:^(BOOL finished){
}];
If that code is being run inside of touchesEnded you are blocking the main thread and that is why the animation happens all at once when its done. You will need run this loop in the background to update the view over time. When you update the UI remember to always call back to the main thread.
Some Threading Options:
performSelectorInBackground:withObject:
performSelectorOnMainThread:withObject:waitUntilDone:
Grand Central Dispatch
Threading

Always animate UIView to user's tap location

I have a UIView that I want to animate to the position where the user's finger currently is.
So, when the touch begins, it has to animate to the tap location.
When the touch moves, it has to adjust its destination to the new location.
When the touch ends, it has to finish the animation to the last tap location.
I tried several things but none worked out. I tried creating a new animation with beginsFromCurrentState in touchesMoved, but that didn't work too.
It seems you're complicating this a little bit. If you are implementing touchesMoved, you are wanting a drag. If you are more interested in a tap, then touchesEnded is where you want to implement this. Either way, you just need to get the location in view and update your UIView's center based upon that location. If you wrap the change to the center value in a UIView animation block, it will animate to the position as it updates.
If you are using iOS 3.2 or later, you can use gesture recognizers which makes this pretty trivial:
// In viewDidLoad or some such
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(viewWasTapped:)];
[[self view] addGestureRecognizer:tapRecognizer];
Then declare your action selector:
- (void)viewWasTapped:(UITapGestureRecognizer*)recognizer
{
CGPoint location = [recognizer locationInView:[self view]];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0f]; // take a second to animate
[viewToMove setCenter:location];
[UIView commitAnimations];
}
This will animate the view from one location to another. This is referred to as implicit animation. Using Core Animation classes such CAKeyframeAnimation or CABasciAnimation would be explicit animation.
Need a few more details for this.. But are you sure you are using touchesBegan, touchesMoved and touchesEnded from the appropriate view (ie not the view you want to move)? Also make sure the userInteractionEnabled = YES. Try logging the touches from touchesBegan to make sure you are receiving the touches.
Or you might want to just check out Apples MoveMe sample code at https://developer.apple.com/library/ios/#samplecode/MoveMe/Introduction/Intro.html where they have solved all those problems for you.

iPhone Dev: Animating PNG Sequences

What is the best or recommended technique for animating PNG Sequences.
Heres what I've learned:
Do it Manually
Using MutableArrays containing Strings, you can animate a UIImageView with a timer which increments an index number
UIImage - animation methods
This works, the only problem is to find if an image has completed its animation, you have to check the isAnimating BOOL, and to do that you need a timer.
What is the best and recommended?
Looking to do Oldschool game sprite animations,
ie:
Idle Animation
Attack Animation
Walk Animation
ect...
Let me know if any one has something.
#lessfame
Example to animate arrows
UIImage* img1 = [UIImage imageNamed:#"fleche1.png"];
UIImage* img2 = [UIImage imageNamed:#"fleche2.png"];
NSArray *images = [NSArray arrayWithObjects:img1,img2, nil];
UIImageView* imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 160.0)];
[imageView setAnimationImages:images];
[imageView setAnimationRepeatCount:100];
[imageView setAnimationDuration:3.0];
imageView.center = myView.center;
[imageView startAnimating];
[myView addSubview:imageView];
[imageView release];
Easiest way is to use Core Animation layers to do sprite animation:
Make a 'multi-cell' image strip (could be PNG or JPG) of all the various moves for the sprite. Make each 'cell' be a fixed height or width.
Put the image in a UIImageView.
Take the CALayer of the view, and adjust the contentsRect property of the layer. This acts as a clipping rectangle for the sprite. To animate sprite all you have to do is move the contentsRect over to the next cell position in the image strip.
Something like this will do it (assuming you've already calculated the newSpritePosition.
[CATransaction begin];
[CATransaction setDisableActions:YES];
spriteLayer.contentsRect = newSpritePosition;
[CATransaction commit];
(If you don't do the second line, then you'll get a default slide animation instead of a 'jump' to the next state.)
With this technique you can rotate through all the frames of the sprite -- much like a flipbook. CALAyer transactions are optimized so you should get pretty fast frame rates. You can set an animationDidStop completion handler to move to the next state of the sprite or loop back to the beginning frame.
It depends on how you are going to use the sprites. If you just need a simple looping sprite then the UIImage method is great. If you want more granular control then you will be happier loading the images into an array and cycling them using a NSTimer to handle the timing. Personally I use the array method most often because it leaves me more options in the future (like detecting when animations have completed). One more suggestion would be to check out the cocos2d project. Its sprite is alot more powerful than either of these suggestions.
As I said in another answer, I found this to be a good way: PNG Animation method by Moses DeJong
From his words:
This example implements an animation oriented view controller that simply waits to read the PNG image data for a frame until it is needed. Instead of alllocating many megabytes, this class run in about a half a meg of memory with about a 5-10% CPU utilization on a 2nd gen iPhone.
I'm still unsure exactly how it's done. I believe it basically uses UIImageView to cache up AVAudioPlayer.