Keeping a Core Animation transition persistent? - iphone

I have a custom UILabel that I want to add a transition to when the text changes.
I create a transition:
CATransition *a = [CATransition animation];
a.duration = 1.0;
a.type = kCATransitionPush;
self.transition = a;
I set the transition to a property in my class. Here's the method I call to update the text:
- (void)postMessage:(NSString *)message {
[self.messageLabel.layer addAnimation:self.transition forKey:#"statusAnimation"];
self.messageLabel.text = message;
}
This all works fine. However, I don't want to keep adding the animation to my label. That slows it down every time I update.
My question is - how can I keep this transition persistent so it shows an animation every single time I call this method without having to add it manually every time?

What you're asking isn't quite possible. Core Animation is a stateful library, and copies the animation object you present, then releases it upon completion. If you are having issues with performance, you should be using the animation block methods. This overview of Core Animation with blocks should help you get started.

Related

can somone explain this syntax for me [Animation<double> animation = new Tween<double>();]

This sound nooby but i had to ask...
Animation<double> animation = new Tween<double>();
what am not understanding here is, why are we creating a Tween instance but assigning it to a reference variable,animation, of type Animation?
From my understaing, Animation is kind of a holder for a specific animation (like Tween).
You need to call the .animate() method in Tween to return an Animation object.
Like: Tween<double>().animate( ... )
So with this code, you are creating a Tween effect, and assigning it to tha animation holder.
The Animation have a status. With this you can check the current status of this animation.
The Animation also "let other objects listen for changes".
Check this documentation:
https://api.flutter.dev/flutter/animation/Animation-class.html
https://api.flutter.dev/flutter/animation/Tween-class.html

Removing CAKeyframeAnimation doesn't release the memory

I'm adding a CAKeyframeAnimation to the layer of an ImageView for representing the animation of an image:
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"contents"];
animation.values = arrayOfImages;
[animation setRemovedOnCompletion:YES];
animation.delegate = self;
[animation setValue:delegate forKey:#"AnimatedImageViewDelegate"];
animation.repeatCount = repeatCount;
animation.fillMode = kCAFillModeForwards;
animation.calculationMode = kCAAnimationDiscrete;
[animation setValue:animationName forKey:#"name"];
When I want to start the animation I call:
animation.duration = duration;
[self.layer addAnimation:animation forKey:animationName];
[self.layer setContents:[animation.values lastObject]];
When the animation finishes, I want to completely remove it and release its memory.
[self.layer removeAllAnimations];
[self.layer removeAnimationForKey:animationName];
Doing this, and using Instruments-Activity Monitor, the memory is not relased unless I release the complete imageView.
How can I release that animation memory without destroy the UIImageView???
You are using the convenience method to instantiate the object. So you will not have control over when the object is actually released. To have control over that, you will need to alloc the object and dealloc it yourself:
Create the instance:
CAKeyframeAnimation *animation = [[CAKeyframeAnimation alloc] init];
Then after the animation finished call after your remove lines:
[animation release];
First off, if this is an iOS 5 project ARC should be handling your releases for you - which is really nice!
Now -- I used a lot of animations in my project and noticed similar bursts in the heap. My guess is that CA caches a lot of what is animated internally. To confirm those items are actually being autoreleased I recommend setting up a test for allocations or leaks with the instruments tool and do the following:
Determine a situation where you would trigger and the animation.
Mark the heap after the animation completes.
Trigger something that would take you to another view where the objects should have been removed.
Repeat the process in steps 1-2-3 as many times as you want. I normally do it about 10 times or so.
That's it.. basically animate - mark - undo - animate - mark. You should see the heapshots decreasing to 0 or almost 0 if CoreAnimation is autoreleasing objects correctly!
For more info on how to test like this take a look at this article:
http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
I have same problem about memory with CAKeyframeAnimation when set values = array of images.
And i see that the problem i get is that the images are inited by [UIImage imagewithname:] which is cached to memory by default. So my solution is change to use [UIImage imageWithContentsOfFile:]
Hope this helpful.
In my case the CAKeyframeAnimation added to an imageView and container view is not getting released even that its not on the view hierarchy.
It turned out that the CAKeyframeAnimation delegate was holding it. I just removed the delegate:
beatAnimation.delegate = nil

iPhone SDK: Block Animation Ignore Error with Curved Path

I'm creating an application where the user can draw a line on a screen from an object to the location they want to send it and the object will then follow the line to the final location. In order to do this, I've already created working methods to allow the user to draw the lines and then to store the coordinates of the line in a MutableArray. However, I'm having some trouble when I try to animate. As I'm pretty new to the iPhone OS, this could be a simple problem, but I haven't been able to find a solution yet.
I am NOT using Bezier Paths as the user is drawing the line manually, I'm not drawing it programmatically.
Here's the code that I've tried
-(void)animateButtonWasPressed
{
for (int f = 0; f < [cordArrayY count]; f++) {
NSString *newY = [cordArrayY objectAtIndex:f];
NSString *newX = [cordArray objectAtIndex:f];
[self myAnimate:newX :newY];
}
}
-(void)myAnimate:(NSString *)PntX :(NSString *)PntY
{
[UIView animateWithDuration:.5 animations:
^{
object.center = CGPointMake([PntX floatValue], [PntY floatValue]);
}];
}
SYNTAX:
object - the object I am trying to move
cordArray - the mutable array containing the x-coordinates
cordArrayY - the mutable array containing the y-coordinates
Everything else is either defined within the code or Apple methods
The problem: the object moves instantly from its original location directly to the final location. I get a NSLog which tells me this:
-[UIApplication endIgnoringInteractionEvents] called without matching -beginIgnoringInteractionEvents. Ignoring.
Any help would be appreciated!
The method you're using to animate "object" seems to be ok. I believe the problem is the loop in which you are invoking that method. You are trying to animate the same property of the object over and over in every step of that loop. I think this causes that "jump".
Take a look at this quote from Apple's docs:
Important: Changing the value of a property while an animation
involving that property is already in progress does not stop the
current animation. Instead, the current animation continues and
animates to the new value you just assigned to the property.
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6
As a consequence of invoking the animation in each step, you will end up animating object in approximately 0.5 secs to the last position of your coordinates array.
I think you should link those animations together, but you should wait for each animation to finish to start the following one. Take a look at this
Another thing that both the animateWithDuration:animations:completion:
and animateWithDuration:delay:options:animations:completion: methods
support is the ability to specify a completion handler block. You
might use a completion handler to signal your application that a
specific animation has finished. Completion handlers are also the way
to link separate animations together.
Hope this helps,
Cheers.
I was finally able to solve this problem:
At first I tried to continue using the method that I had above. Lio's advice to use the "completion" block was perfect, but as I needed it to loop for an undefined number of times, I would have to use a counter variable. iPhone block programming doesn't allow the modification of external variables or the use of the _block declaration, so this didn't work out for me.
However, I eventually created my entire animation using NSTimer in the method described here just using my array coordinates instead:
http://www.icodeblog.com/2008/10/28/iphone-programming-tutorial-animating-a-ball-using-an-nstimer/

UIView animations completion block firing when an other animation begins

I use the block base API for my animations on iOS.
One animation has a completion block and that block is called at the end of the animation, nice.
However, that animation can be fired multiple times when the user scrolls (the animation is on a UITableViewCell). When that happens the the completion block is called multiple times. The finished parameter of the block is always YES.
Since the animation is not actually finished (an other animation took place) I thought that the finished parameter would be NO, but it's not.
Did I miss something? How can I avoid the completion block to be called multiple times?
The completion block is called multiple times simply because, in your case, your animation is fired multiple times. What is happening is that iOS invokes your animation block each time it is told so, probably in a separate thread. Then, for each animation it tracks its completion, and upon completion it calls the associated completion block. So basically, you see your completion block firing multiple times, one for each invocation of your animation. Note that the boolean value associated to a completion block is specific of that completion block, it does not refer in any way to a different animation.
To recap, what you are experiencing is simply the effect of concurrency. If this is not your intended behavior, then you need to modify your code accordingly. If you want your animations to fire one at a time, you may use NSLock (NSConditionLock for advanced control using an associate condition variable) or, if you prefer, a mutex and the Posix pthreads library directly, to create a critical section to be executed in a mutually exclusive fashion.
Not sure when you're firing the animations and whether they loop (like a UIActivityView spinner or something) - sounds like it's every single pixel the table view is scrolling?
In any event, perhaps you could use the UIScrollView delegate methods and tell each cell to start animation on scrollViewWillBeginDragging: and tell each cell to stop at scrollViewDidEndDragging:
You could set a boolean isAnimating for your UITableViewCell and if an animation is currently underway, do nothing.
if (isAnimating) {
// ... do nothing
} else {
// Start your animation
}
Or stick with whatever you have now and use a boolean still, but only fire the animation if it's not currently animating. Then in your finished parameter just set isAnimating to NO.
if (isAnimating) {
// ... do nothing
} else {
[UIView animateWithDuration:0.3f
animations:^{
// animations...
isAnimating = YES;
}
completion:^{
isAnimating = NO;
}
];
}
I've resolved this issue by looking if the completion block is relevant at the beginning of that block.
The finished parameter is not relevant right now. I've communicated with Apple and they told me that it's fixed in iOS 4.2.

How to group two CABasicAnimation animations and kick them off at the exact same time?

I know there is some kind of animation grouping mechanism in core animation. So lets say I have two CABasicAnimation firstAnimation and secondAnimation. How would I group these and how would I kick off the group to start animating?
You'll want to use the CAAnimationGroup class. Create an array containing the animations you want, and set the AnimationGroup's animations property to that array. CAAnimationGroup is a subclass of CAAnimation, so you can add it to a layer using [layer addAnimation:forKey:] like you would a regular animation. Once added to a layer, all animations in a group execute concurrently.
I would suggest reading the CAAnimationGroup Reference first. There are a number of implementation details worth understanding before you use it. For example:
The delegate property of individual animations is ignored.
The removeOnCompletion property of individual animations is ignored.
The AnimationGroup has its own delegate and removeOnCompletion properties.
Animations aren't time-scaled to the group, so if an individual animation has a duration longer than that of the group object, it will be interrupted at the end of the group's duration.
The animations property of CAAnimationGroup is copied, not retained.