How To Apply Complex CALayer Transform Changes? - ios

I need a complex continuous animation of a UIView that involves setting CATransform3D rotation and translation properties that need to be calculated, so a standard animation is no option.
I turned to using CALayer animation. And have this:
self.displayLink = [self.window.screen displayLinkWithTarget:self selector:#selector(update:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- (void)update:(CADisplayLink*)sender
{
CGFloat elapsedTime = sender.timestamp - self.lastTimestamp;
self.lastTimestamp = sender.timestamp;
self.rotation += elapsedTime * 0.1; // Factor determines speed.
// This is just example for SO post; the real animation is more complicated!
CATransform3D transform;
transform = CATransform3DMakeRotation(self.rotation, 1.0, 0.0, 0.0);
self.imageLayer.transform = transform;
}
Here self.imageLayer is a CALayer whose contents has been set with an image and added as a sublayer to my base-view.
It does rotate 'a bit' but not continuously, sometimes it seems to stop or rotate backwards a bit.
It seems that assigning a new transform quite often does not have any effect because the self.rotation value is incremented much much more. Adding a [self setNeedsDisplay] did not help.
I've not done much with CALayers yet so I guess that I'm missing something very basic. Tried to find it for some time but all examples I found seem too far from what I want.

When you create your own layer and then modify its properties, it animates them implicitly (see “Animating Simple Changes to a Layer’s Properties”. You're not seeing the changes you expect because Core Animation is animating the changes rather than applying them instantly.
You need to disable implicit animation. There are several ways to disable it; see “Changing a Layer’s Default Behavior”. Or search stack overflow for “disable implicit animations”. Here's one way:
NSDictionary *actions = #{ #"transform": [NSNull null] };
self.imageLayer.actions = actions;
Just do that once, where you create imageLayer.

Related

how to reset/restart an animation and have it appear continuous?

So, I am fairly new to iOS programming, and have inherited a project from a former coworker. We are building an app that contains a gauge UI. When data comes in, we want to smoothly rotate our "layer" (which is a needle image) from the current angle to a new target angle. Here is what we have, which worked well with slow data:
-(void) MoveNeedleToAngle:(float) target
{
static float old_Value = 0.0;
CABasicAnimation *rotateCurrentPressureTick = [CABasicAnimation animationWithKeyPath:#"transform.rotation");
[rotateCurrentPressureTick setDelegate:self];
rotateCurrentPressureTick.fromValue = [NSSNumber numberWithFloat:old_value/57.2958];
rotateCurrentPressureTick.removedOnCompletion=NO;
rotateCurrentPressureTick.fillMode=kCAFillModeForwards;
rotateCurrentPressureTick.toValue=[NSSNumber numberWithFloat:target/57.2958];
rotateCurrentPressureTick.duration=3; // constant 3 second sweep
[imageView_Needle.layer addAnimation:rotateCurrentPressureTick forKey:#"rotateTick"];
old_Value = target;
}
The problem is we have a new data scheme in which new data can come in (and the above method called) faster, before the animation is complete. What's happening I think is that the animation is restarted from the old target to the new target, which makes it very jumpy.
So I was wondering how to modify the above function to add a continuous/restartable behavior, as follows:
Check if the current animation is in progress and
If so, figure out where the current animation angle is, and then
Cancel the current and start a new animation from the current rotation to the new target rotation
Is it possible to build that behavior into the above function?
Thanks. Sorry if the question seems uninformed, I have studied and understand the above objects/methods, but am not an expert.
Yes you can do this using your existing method, if you add this bit of magic:
- (void)removeAnimationsFromView:(UIView*)view {
CALayer *layer = view.layer.presentationLayer;
CGAffineTransform transform = layer.affineTransform;
[layer removeAllAnimations];
view.transform = transform;
}
The presentation layer encapsulates the actual state of the animation. The view itself doesn't carry the animation state properties, basically when you set an animation end state, the view acquires that state as soon as you trigger the animation. It is the presentation layer that you 'see' during the animation.
This method captures the state of the presentation layer at the exact moment you cancel the animation, and then applies that state to the view.
Now you can use this method in your animation method, which will look something like this:
-(void) MoveNeedleToAngle:(float) target{
[self removeAnimationsFromView:imageView_Needle];
id rotation = [imageView_Needle valueForKeyPath:#"layer.transform.rotation.z"];
CGFloat old_value = [rotation floatValue]*57.2958;
// static float old_value = 0.0;
CABasicAnimation *rotateCurrentPressureTick = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
[rotateCurrentPressureTick setDelegate:self];
rotateCurrentPressureTick.fromValue = [NSNumber numberWithFloat:old_value/57.2958];
rotateCurrentPressureTick.removedOnCompletion=NO;
rotateCurrentPressureTick.fillMode=kCAFillModeForwards;
rotateCurrentPressureTick.toValue=[NSNumber numberWithFloat:target/57.2958];
rotateCurrentPressureTick.duration=3; // constant 3 second sweep
[imageView_Needle.layer addAnimation:rotateCurrentPressureTick forKey:#"rotateTick"];
old_value = target;
}
(I have made minimal changes to your method: there are a few coding style changes i would also make, but they are not relevant to your problem)
By the way, I suggest you feed your method in radians, not degrees, that will mean you can remove those 57.2958 constants.
You can get the current rotation from presentation layer and just set the toValue angle. No need to keep old_value
-(void) MoveNeedleToAngle:(float) targetRadians{
CABasicAnimation *animation =[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.duration=5.0;
animation.fillMode=kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation.removedOnCompletion=NO;
animation.fromValue = [NSNumber numberWithFloat: [[layer.presentationLayer valueForKeyPath: #"transform.rotation"] floatValue]];
animation.toValue = [NSNumber numberWithFloat:targetRadians];
// layer.transform= CATransform3DMakeRotation(rads, 0, 0, 1);
[layer addAnimation:animation forKey:#"rotate"];
}
Another way i found (commented line above) is instead of using fromValue and toValue just set the layer transform. This will produce the same animation but the presentationLayer and the model will be in sync.

Apply a Transform to a UIView Itself

I'm trying to apply a perspective transform to my UIView object with the following code:
CATransform3D t = CATransform3DIdentity;
t.m34 = -1.0/1000;
t = CATransform3DRotate(t, angle, 0.0f, 1.0f, 0.0f);
myView.layer.transform = t;
and I don't see any effect at all. I tried other transforms like a simple translation and they don't work either.
However, if I do either of the following two modifications then it will work somewhat but neither satisfies my request:
Change the last line to
myView.layer.sublayerTransform = t;
This sort of works but it only transforms the subviews on myView, not myView itself.
Add an animation code to apply the change instead of directly assign the change to the layer:
CABasicAnimation *turningAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
turningAnimation.toValue = [NSValue valueWithCATransform3D:t];
turningAnimation.delegate = self;
turningAnimation.fillMode = kCAFillModeForwards;
turningAnimation.removedOnCompletion = NO;
[myView.layer addAnimation:turningAnimation forKey:#"turning"];
The thing is that I don't want the animation.
Can anybody point a direction for me?
Thanks!
You should be able to just use myView.transform = CGAffineTransformMakeRotation(angle). It won't animate the property unless you explicitly tell it to using [UIView animateWithDuration:]. Using the UIView transform property will ultimately apply your transformation to the CALayer that backs UIView, so I would make sure to only interact with the transforms on one level (UIView or CALayer).
UIView transform doc:
https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/UIView/UIView.html#//apple_ref/occ/instp/UIView/transform
CGAffineTransform Doc:
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html#//apple_ref/c/func/CGAffineTransformMakeRotation
I figured out the problem after test by Krishnan confirmed that layer.transform itself does work without the aid of animation or sublayerTransform. The problem I encountered was caused by an animation that had been applied to my view's layer and was not subsequently removed. Since this animation had a toValue equal to the identity transform, I didn't realize it can actually prevent subsequent non-animated transforms from working. Setting removedOnCompletion = YES (which is default) on the animation solved the mystery.

Get instant value of a CABasicAnimation

While i'm implementing a game, i'm just front of a matter, whose really easy i think, but don't know how to fix it. I'm a bit new in objective-c as you could see with my reputation :(
The problem is, i have an animation, which works correctly. Here is the code :
CABasicAnimation * bordgauche = [CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
bordgauche.fromValue = [NSNumber numberWithFloat:0.0f];
bordgauche.toValue = [NSNumber numberWithFloat:749.0f];
bordgauche.duration = t;
bordgauche.repeatCount = 1;
[ImageSuivante.layer addAnimation:bordgauche forKey:#"bordgauche"];
And i want to get the current position of my image. So i use :
CALayer *currentLayer = (CALayer *)[ImageSuivante.layer presentationLayer];
currentX = [(NSNumber *)[currentLayer valueForKeyPath:#"transform.translation.x"] floatValue];
But i don't get it instantly. I get one time "Current = 0.0000", which is the starting value, when i use a nslog to print it, but not the others after.
I don't know how to get the instant position of my image, currentX, all the time.
I expect i was understable.
Thanks for your help :)
You can get the value from your layer's presentation layer.
CGPoint currentPos = [ImageSuivante.layer.presentationLayer position];
NSLog(#"%f %f",currentPos.x,currentPos.y);
I think you have 3 options here (pls comment if more exist):
option1: split your first animation into two and when the first half ends start the second half of the animation plus the other animation
...
bordgauche.toValue = [NSNumber numberWithFloat:749.0f / 2];
bordgauche.duration = t/2;
bordgauche.delegate = self // necessary to catch end of anim
[bordgauche setValue:#"bordgauche_1" forKey: #"animname"]; // to identify anim if more exist
...
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
if ([theAnimation valueForKey: #"animname"]==#"bordgauche_1") {
CABasicAnimation * bordgauche = [CABasicAnimation
animationWithKeyPath:#"transform.translation.x"];
bordgauche.fromValue = [NSNumber numberWithFloat:749.0f / 2];
bordgauche.toValue = [NSNumber numberWithFloat:749.0f];
bordgauche.duration = t/2;
bordgauche.repeatCount = 1;
[ImageSuivante.layer addAnimation:bordgauche forKey:#"bordgauche_2"];
// plus start your second anim
}
option2: setup a NSTimer or a CADisplayLink (this is better) callback and check continuously the parameters of your animating layer. Test the parameters for the required value to trigger the second anim.
displayLink = [NSClassFromString(#"CADisplayLink") displayLinkWithTarget:self selector:#selector(check_ca_anim)];
[displayLink setFrameInterval:1];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
... or
animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(1.0 / 60.0) target:self selector:#selector(check_ca_anim) userInfo:nil repeats:TRUE];
[[NSRunLoop mainRunLoop] addTimer:animationTimer forMode:NSRunLoopCommonModes];
- (void) check_ca_anim {
...
CGPoint currentPosition = [[ImageSuivante.layer presentationLayer] position];
// test it and conditionally start something
...
}
option3: setup a CADisplayLink (can be called now as "gameloop") and manage the animation yourself by calculating and setting the proper parameters of the animating object. Not knowing what kind of game you would like to create I would say game loop might be useful for other game specific reasons. Also here I mention Cocos2d which is a great framework for game development.
You can try getting the value from your layer's presentation layer, which should be close to what is being presented on screen:
[ [ ImageSuivante.layer presentationLayer ] valueForKeyPath:#"transform.translation.x" ] ;
I would add one option to what #codedad pointed out. What's interesting, it gives a possibility of getting instant values of your animating layer precisely (you can see CALayer's list of animatable properties) and it's very simple.
If you override method
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
in your UIView class (yep, you'd need to implement a custom class derived from UIView for your "ImageSuivante" view), then the parameter layer which is passed there would give you exactly what you need. It contains precise values used for drawing.
E.g. in this method you could update a proper instant variable (to work with later) and then call super if you don't do drawing with you hands:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
self.instantOpacity = layer.opacity;
self.instantTransform = layer.transform;
[super drawLayer:layer inContext:context];
}
Note that layer here is generally not the same object as self.layer (where self is your custom UIView), so e.g. self.layer.opacity will give you the target opacity, but not the instant one, whereas self.instantOpacity after the code above will contain the instant value you want.
Just be aware that this is a drawing method and it may be called very frequently, so no unnecessary calculations or any heavy operations there.
The source of this idea is Apple's Custom Animatable Property project.

iOS CAKeyframeAnimation rotationMode on UIImageView

I am experimenting with Key Frame animation of the position of a UIImageView object moving along a bezier path. This pic shows the initial state before animation. The blue line is the path - initially moving straight up, the light green box is the initial bounding box or the image, and the dark green "ghost" is the image that I am moving:
When I kick off the animation with rotationMode set to nil, the image keeps the same orientation all the way through the path as expected.
But when I kick off the animation with rotationMode set to kCAAnimationRotateAuto, the image immediately rotates 90 degrees anti-clockwise and keeps this orientation all the way through the path. When it reaches the end of the path it redraws in the correct orientation (well it actually shows the UIImageView that I repositioned in the final location)
I was naively expecting that the rotationMode would orientate the image to the tangent of the path and not to the normal, especially when the Apple docs for the CAKeyframeAnimation rotationMode state
Determines whether objects animating along the path rotate to match the path tangent.
So what is the solution here? Do I have to pre-rotate the image by 90 degrees clockwise? Or is there something that I am missing?
Thanks for your help.
Edit 2nd March
I added a rotation step before the path animation using an Affine rotation like:
theImage.transform = CGAffineTransformRotate(theImage.transform,90.0*M_PI/180);
and then after the path animation, resetting the rotation with:
theImage.transform = CGAffineTransformIdentity;
This makes the image follow the path in the expected manner. However I am now running into a different problem of the image flickering. I am already looking for a solution to the flickering issue in this SO question:
iOS CAKeyFrameAnimation scaling flickers at animation end
So now I don't know if I have made things better or worse!
Edit March 12
While Caleb pointed out that yes, I did have to pre rotate my image, Rob provided an awesome
package of code that almost completely solved my problems. The only thing that Rob didn't do was compensating for my assets being drawn with a vertical rather than horizontal orientation, thus still requiring to preRotate them by 90 degrees before doing the animation. But hey, its only fair that I have to do some of the work to get things running.
So my slight changes to Rob's solution to suite my requirements are:
When I add the UIView, I pre Rotate it to counter the inherent rotation added by setting the rotationMode:
theImage.transform = CGAffineTransformMakeRotation(90*M_PI/180.0);
I need to keep that rotation at the end of the animation, so instead of just blasting the view's transform with a new scale factor after the completion block is defined, I build the scale based on the current transform:
theImage.transform = CGAffineTransformScale(theImage.transform, scaleFactor, scaleFactor);
And that's all I had to do to get my image to follow the path as I expected!
Edit March 22
I have just uploaded to GitHub a demo project that shows off the moving of an object along a bezier path. The code can be found at PathMove
I also wrote about it in my blog at Moving objects along a bezier path in iOS
The issue here is that Core Animation's autorotation keeps the horizontal axis of the view parallel to the path's tangent. That's just how it works.
If you want your view's vertical axis to follow the path's tangent instead, rotating the contents of the view as you're currently doing is the reasonable thing to do.
Here's what you need to know to eliminate the flicker:
As Caleb sort of pointed out, Core Animation rotates your layer so that its positive X axis lies along the tangent of your path. You need to make your image's “natural” orientation work with that. So, supposing that's a green spaceship in your example images, you need the spaceship to point to the right when it doesn't have rotation applied to it.
Setting a transform that includes rotation interferes with the rotation applied by `kCAAnimationRotateAuto'. You need to remove the rotation from your transform before applying the animation.
Of course that means you need to reapply the transformation when the animation completes. And of course you want to do that without seeing any flicker in the appearance of the image. That's not hard, but there some secret sauce involved, which I explain below.
You presumably want your spaceship to start out pointing along the tangent of the path, even when the spaceship is sitting still having not been animated yet. If your spaceship image is pointing to the right, but your path goes up, then you need to set the transform of the image to include a 90° rotation. But perhaps you don't want to hardcode that rotation - instead you want to look at the path and figure out its starting tangent.
I'll show some of the important code here. You can find my test project on github. You may find some use in downloading it and trying it out. Just tap on the green “spaceship” to see the animation.
So, in my test project, I have connected my UIImageView to an action named animate:. When you touch it, the image moves along half of a figure 8 and doubles in size. When you touch it again, the image moves along the other half of the figure 8 (back to the starting position), and returns to its original size. Both animations use kCAAnimationRotateAuto, so the image points along the tangent of the path.
Here's the start of animate:, where I figure out what path, scale, and destination point the image should end up at:
- (IBAction)animate:(id)sender {
UIImageView* theImage = self.imageView;
UIBezierPath *path = _isReset ? _path0 : _path1;
CGFloat newScale = 3 - _currentScale;
CGPoint destination = [path currentPoint];
So, the first thing I need to do is remove any rotation from the image's transform, since as I mentioned, it will interfere with kCAAnimationRotateAuto:
// Strip off the image's rotation, because it interferes with `kCAAnimationRotateAuto`.
theImage.transform = CGAffineTransformMakeScale(_currentScale, _currentScale);
Next, I go into a UIView animation block so that the system will apply animations to the image view:
[UIView animateWithDuration:3 animations:^{
I create the keyframe animation for the position and set a couple of its properties:
// Prepare my own keypath animation for the layer position.
// The layer position is the same as the view center.
CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
positionAnimation.path = path.CGPath;
positionAnimation.rotationMode = kCAAnimationRotateAuto;
Next is the secret sauce for preventing flicker at the end of the animation. Recall that animations do not effect the properties of the “model layer“ that you attach them to (theImage.layer in this case). Instead, they update the properties of the “presentation layer“, which reflects what's actually on the screen.
So first I set removedOnCompletion to NO for the keyframe animation. This means the animation will stay attached to the model layer when the animation is complete, which means I can access the presentation layer. I get the transform from the presentation layer, remove the animation, and apply the transform to the model layer. Since this is all happening on the main thread, these property changes all happen in one screen refresh cycle, so there's no flicker.
positionAnimation.removedOnCompletion = NO;
[CATransaction setCompletionBlock:^{
CGAffineTransform finalTransform = [theImage.layer.presentationLayer affineTransform];
[theImage.layer removeAnimationForKey:positionAnimation.keyPath];
theImage.transform = finalTransform;
}];
Now that I've set up the completion block, I can actually change the view properties. The system will automatically attach animations to the layer when I do this.
// UIView will add animations for both of these changes.
theImage.transform = CGAffineTransformMakeScale(newScale, newScale);
theImage.center = destination;
I copy some key properties from the automatically-added position animation to my keyframe animation:
// Copy properties from UIView's animation.
CAAnimation *autoAnimation = [theImage.layer animationForKey:positionAnimation.keyPath];
positionAnimation.duration = autoAnimation.duration;
positionAnimation.fillMode = autoAnimation.fillMode;
and finally I replace the automatically-added position animation with the keyframe animation:
// Replace UIView's animation with my animation.
[theImage.layer addAnimation:positionAnimation forKey:positionAnimation.keyPath];
}];
Double-finally I update my instance variables to reflect the change to the image view:
_currentScale = newScale;
_isReset = !_isReset;
}
That's it for animating the image view with no flicker.
And now, as Steve Jobs would say, One Last Thing. When I load the view, I need to set the transform of the image view so that it's rotated to point along the tangent of the first path that I will use to animate it. I do that in a method named reset:
- (void)reset {
self.imageView.center = _path1.currentPoint;
self.imageView.transform = CGAffineTransformMakeRotation(startRadiansForPath(_path0));
_currentScale = 1;
_isReset = YES;
}
Of course, the tricky bit is hidden in that startRadiansForPath function. It's really not that hard. I use the CGPathApply function to process the elements of the path, picking out the first two points that actually form a subpath, and I compute the angle of the line formed by those two points. (A curved path section is either a quadratic or cubic bezier spline, and those splines have the property that the tangent at the first point of the spline is the line from the first point to the next control point.)
I'm just going to dump the code here without explanation, for posterity:
typedef struct {
CGPoint p0;
CGPoint p1;
CGPoint firstPointOfCurrentSubpath;
CGPoint currentPoint;
BOOL p0p1AreSet : 1;
} PathState;
static inline void updateStateWithMoveElement(PathState *state, CGPathElement const *element) {
state->currentPoint = element->points[0];
state->firstPointOfCurrentSubpath = state->currentPoint;
}
static inline void updateStateWithPoints(PathState *state, CGPoint p1, CGPoint currentPoint) {
if (!state->p0p1AreSet) {
state->p0 = state->currentPoint;
state->p1 = p1;
state->p0p1AreSet = YES;
}
state->currentPoint = currentPoint;
}
static inline void updateStateWithPointsElement(PathState *state, CGPathElement const *element, int newCurrentPointIndex) {
updateStateWithPoints(state, element->points[0], element->points[newCurrentPointIndex]);
}
static void updateStateWithCloseElement(PathState *state, CGPathElement const *element) {
updateStateWithPoints(state, state->firstPointOfCurrentSubpath, state->firstPointOfCurrentSubpath);
}
static void updateState(void *info, CGPathElement const *element) {
PathState *state = info;
switch (element->type) {
case kCGPathElementMoveToPoint: return updateStateWithMoveElement(state, element);
case kCGPathElementAddLineToPoint: return updateStateWithPointsElement(state, element, 0);
case kCGPathElementAddQuadCurveToPoint: return updateStateWithPointsElement(state, element, 1);
case kCGPathElementAddCurveToPoint: return updateStateWithPointsElement(state, element, 2);
case kCGPathElementCloseSubpath: return updateStateWithCloseElement(state, element);
}
}
CGFloat startRadiansForPath(UIBezierPath *path) {
PathState state;
memset(&state, 0, sizeof state);
CGPathApply(path.CGPath, &state, updateState);
return atan2f(state.p1.y - state.p0.y, state.p1.x - state.p0.x);
}
Yow mention that you kick off the animation with "rotationMode set to YES", but the documentation states that rotationMode should be set using an NSString...
In particular:
These constants are used by the rotationMode property.
NSString * const kCAAnimationRotateAuto
NSString * const kCAAnimationRotateAutoReverse
Have you tried setting:
keyframe.animationMode = kCAAnimationRotateAuto;
The documentation states:
kCAAnimationRotateAuto: The objects travel on a tangent to the path.

CAlayer transform on sublayers flickers with gestures (ipad)

I have a CALayer that contains few other subLayers (CATextLayer actually)
I want to apply some transformation on that layer when user do usual gesture on the ipad but it doesn't seem to be working properly. The goal of using the CALayer was to apply the transformation only to that Layer so that all of my sub-textLayer would be affected at the same time with the same transformation.
What's happening is that the transformation seem to flicker between previous and current position. I don't really get what could be the problem... When I do a 2 fingers panning gesture for example, CaTextLayer positions flickers all the time during my gesture and at the end they are all correctly placed at their new translated position.
So everything seems to work fine except that flickering thing that is bothering me a lot.
Do I need to set some property I don't know about ? I'm thinking it might have something to do with bounds and frame too....
Here's how I create my CATextLayer (This is done only once at creation and it works properly):
_textString = [[NSString alloc] initWithString: text];
_position = position;
attributedTextLayer_ = [[CATextLayer alloc] init];
attributedTextLayer_.bounds = frameSize;
//.. Set the font
attributedTextLayer_.string = attrString;
attributedTextLayer_.wrapped = YES;
CFRange fitRange;
CGRect textDisplayRect = CGRectInset(attributedTextLayer_.bounds, 10.f, 10.f);
CGSize recommendedSize = [self suggestSizeAndFitRange:&fitRange
forAttributedString:attrString
usingSize:textDisplayRect.size];
[attributedTextLayer_ setValue:[NSValue valueWithCGSize:recommendedSize] forKeyPath:#"bounds.size"];
attributedTextLayer_.position = _position;
This is how I add them to my Super CALayer
[_layerMgr addSublayer:t.attributedTextLayer_];
[[_drawDelegate UI_GetViewController].view.layer addSublayer:_layerMgr];
And here's how I apply my transformation :
_layerMgr.transform = CATransform3DMakeAffineTransform(_transform);
After lots of reading and testing...I've found my own solution.
It looks like the CoreAnimation uses default animation when you do transformation or operation on any layers. It's very recommended that when you do such CALayer operation that you go through what they call "Transactions".
I've found all about that in the CoreAnimation Programming guide, under the section : transactions.
My solution was then to implement such a transaction, and preventing any animation while doing CALayer operation.
This is what I do when applying my transformation (which prevents flickering) :
-(void)applyTransform
{
if (! CGAffineTransformIsIdentity(_transform))
{
[CATransaction begin];
//This is what prevents all animation during the transaction
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
_layerMgr.transform = CATransform3DMakeAffineTransform(_transform);
[CATransaction commit];
}
}

Resources