CALayer in a UIImageView - iphone

I have a CALayer and I would like to use it as an UIImageView. That way I'll be able to move it with timers etc... so here the layer :
CALayer *rootLayer = [CALayer layer];
And I would like to move it with timer like that :
Image.center=CGPointMake(Image.center.x +10, Image.center.y );

For this you'll need a CABasicAnimation. For the timer, create an NSTimer. Make sure that the timer fires the method on the main thread using one of the performSelectorOnMainThread methods.
That method will add the CABasicAnimation to the layer. You'll have to be careful with the end positions as when the animation finishes, your layer will still be visible in it's original position.
To resolve that you'll need to update the model value of the layer in the animationDidStart delegate callback of the CABasicAnimation.
Note that if it's a repeating animation - which I guess it is given you're using a timer - you can use the removedOnCompletion flag. That means you can re-use the same animation repeatedly. Check out this question for details on how to make use of that:
How to reuse an CABasicAnimation when not removed after completion?

Related

CoreAnimation gets jerking

I've created an UIView and some layers. I've organized those layers into superview-subview hierarchy with the root in UIView's layer, added some gesture recognizers and I am trying to manipulate layers' geometry on events from gesture recognizers (setting bounds and position for sure). I use implicit animations and CATransactions. No explicit animations are used.
The actual result is good enough if I create CATransaction with duration = 0. But if I set the duration to 0.2 I get some strange results: as event come not rapidly and previous transaction have time to be completed before the next starts everything is OK (for example on tapping), but if there is not enough time (for example on pinch or pan) the layer being resized starts jerking. Visually it looks like animation rolls back to the initial point and starts again to the new value.
I do not see any reason for this. I've tried to perform the layout in -[UIView layoutSubviews] and invoke setNeedsDisplay in gesture recognizers handlers. I've also tried to separate this logic into separate method but it does not help.
I repeat once more that I use only implicit animations. And what I want to know is why does it happen (but not how to work around).
Any clues?
I think, it's because you start new animation when previous not finished.
Maybe, you need to stop it like
[yourView.layer removeAnimationForKey:#"yourAnimationKey"];
and you need to implement animation delegate method
-(void)stopAnimation:(id)sender
{
yourView.frame = [[yourView.layer presentationLayer] frame]; // that set frame of your view to it's animation position
}

CATransition as an explicit animation

Greetings to all.
Does anyone know if it is possible to essentially convert CATransition to an explicit animation? That is, I almost want it to behave like CABasicAnimation does- I need to be able to create the CATransition object, set the options for it (duration, timingFunction, delegate, type, fillMode, removedOnCompletion, etc)- then essentially store it for later use and make sure it isn't trying to animate stuff that's happening between the CATransition object creation and the point in time at which I actually need it to animate a set of properties.
If I'm using CABasicAnimation, I can easily do this as nothing is animated until the animation is added to the layer and only the property specified in animationWithKeyPath is animated between toValue/fromValue. However, CATransition apparently fires [CATransaction begin] the moment you create the animation object for capturing property changes.
What I need to be able to do (as per above) is create the CATransition object, set the above options, then put it away until I actually need to use it- at which point I'll change the actual properties that it needs to animate, then add it to the appropriate CALayer via addAnimation.
Does anyone know how to do this safely?
PS: The reason for this is because my application depends heavily on a custom animation queueing system that handles animation dependencies. For example, certain things need to animate before an orientation change occurs, so I can queue ~10 animations which will all fire off AND the orientation change animation object as well- but the orientation change animation won't fire until the previous animations that it depends on have finished. Incidentally, CABasicAnimation can be wrapped up nicely into a queued animation object class. CATransition seems to be a bit of a different beast, hence the above question- I need to be able to safely create an animation queue object, setup a bunch of CATransition options, queue it up, then actually have it execute later on (meanwhile all sorts of stuff is happening) when all of it's dependencies have been satisfied (in this case, CATransition is being used for the orientation change animation).
Thanks,
-Keven Tipping

layoutSubviews during an animation?

I have a UIView with a bunch of subviews, all positioned using layoutSubviews. When the view is resized, the relative positions all change. I'd like these re-calculations to happen during an animated resize (using +[UIView beginAnimations:] calls). This doesn't seem to be happening. Any ideas?
Assumption: You want to have multiple animation steps (i.e. position doesn't change linearly with frame size).
This isn't possible with a single "standard" UIView animations. Why? The frame/bounds is only set once.
Core Animation has three "layer trees":
The model tree is where your app thinks things are.
The presentation tree is approximately what's being displayed on screen.
The render tree is approximately what Core Animation is compositing.
UIView is a (somewhat thin) wrapper around the model layer. During a UIView animation, Core Animation updates the presentation/render tree — the model tree represents the endpoint of animations. The upshot is that your code can (for the most part) treat animations as instantaneous — moving a view from A to B instantly moves it to B; the change just happens to be animated to the user.
There are more complicated things you can do with CALayer/CAAnimation directly, but I haven't investigated this much.
You could chain multiple animations together using -[UIView setAnimationDidStopSelector:]. (You could also try using multiple animations together with setAnimationDelay:, but I'm not sure what happens with multiple animations on the same property; you might have luck with setAnimationBeginsFromCurrentState:.)
If you want really fine-grained control, CADisplayLink (OS 3.1+) is a timer that fires after each screen refresh. A fallback option (for 3.0 support) is to use an NSTimer at 30/60 Hz or so.
I know this is an old question, but this code works for me very well (suited for your example of changing frame).
-(void)layoutSubviews{
[super layoutSubviews];
// layout your subviews here, or whatever
}
-(void)someMethod{
double duration=...;
[UIView animateWithDuration:duration animations:^{
self.frame = ...;
[self layoutIfNeeded];
}];
}
Of course you can call this method from another object. The "trick" is to call layoutIfNeeded (or layoutSubviews directly - same thing, if You change the frame the setNeedsLayout is called).
As tc. nicely explained the "layer trees", You just force the presentation layer to display the final stage of model layer with animation.
The advantage of this method is in possibility to control when the frame/bounds change is animated and when it's instant.
Hope this helps someone:).
Completing #GrizzlyNetch's anwer, you can set the UIViewAnimationOptionLayoutSubviews animation option, so you don't need to call layoutIfNeeded:
-(void)someMethod{
double duration = ...;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
self.frame = ...;
} completion:nil];
}
Posting for completeness. Thanks to tc. for explaining that what I want to do, exactly, is not supported by Core Animation.
I eventually came up with a reasonable solution. Rather then layout my subviews in -layoutSubviews, I do so in -setBounds:. Then, when I wrap a -setBounds: call in a UIView +beginAnimations: block, those positioning calls are also animated, and the end result is everything properly animating to where it should god.

sequencing UIImageView frame(s) animation & CGTransform animation

I'd like help on some strategies to sequence some animations.
I subclassed UIImageView so that I could write some custom animation actions on an image. I implemented a few methods to be used as actions that I could call on my image
example:
-(void)rotateAnim; //rotates the image by a few degrees using a CGAffine Transform
-(void)numbersFlashAnim; //uses the UIImageVew.animationImages array for a 14 frame animation.
-(void)moveLeftAnim; //uses another CGAffine Transform to change the imageView's position.
In my viewDidLoad method I create an instance of my UIImageView subclass. What ways exist to call these animations in sequence?
I was thinking about using an NSTimer to handle the animations, but wasn't sure if you could write an NSTimer object to handle multiple method calls.
example:
[imageView rotateAnim]; //when this animation is done, I want to call:
[imageView numbersFlashAnim];
I've seen several questions regarding the use of an NSTimer, but none that specifically relate to this problem. Note: I saw that the dev docs on apple's site also recommend the use of the performSelector:withObject:afterDelay: in some cases but was wondering if that would offer enough flexibility.
Additionally, I've already taken a look at the Cocos2d framework, and although I can use their methods ~(Sequence actions: etc, ) I'm choosing to solve this problem with UIKit/Foundation, etc.
Do you use animation? I mean you can rotate your image inside
[UIView beginAnimations:(NSString *)animationID context:(void *)context] block. There you can add a delegate method that will be called after current animation ends. And there you can call your numbersFlashAnim.
(Sorry for my english ;-) )

CALayer delegate method drawLayer not getting called

My object a sub class of NSObject has CALayer instance variable. I use it for drawing and caching content with its delegate set to my object.
But for some reason drawLayer:inContext: method NEVER gets called. Whereas actionForLayer:forKey: delegate method does get called implying the delegate is getting set properly with layer.delegate = self in the object's init method.
Any suggestions on what is preventing my layer drawing method drawLayer:inContext: from getting called ?
I am called the [layer setNeedDisplay] often. So I guess it is some fundamental error.
drawLayer:inContext: won't get called if your frame is CGRectZero or offscreen. Also, if your CALayer isn't attached to an existing onscreen layer, it will never draw, no matter how many times you call setNeedsDisplay
Implement an empty drawRect:
- (void)drawRect:(CGRect)rect {
}
Taken from the ZoomingPDFViewer project:-
UIView uses the existence of -drawRect: to determine if it should
allow its CALayer to be invalidated, which would then lead to the
layer creating a backing store and -drawLayer:inContext: being
called. By implementing an empty -drawRect: method, we allow UIKit
to continue to implement this logic, while doing our real drawing work
inside of -drawLayer:inContext:
The layer object's setNeedsDisplay must be called. Simply adding the layer as a sublayer does not do that for you. Got this from Ray Wenderlich's CALayer tutorial.
If you're eventually using the CALayer with a UIView then the delegate must be the view object itself:
From the iOS CALayer documentation:
"In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer."
If you have a multi-threaded app where background processing drives the need to update the CALayer, you must call setNeedsDisplay in the main thread