CATransform3D rotate causes half of image to disappear - iphone

I'm using the following code to rotate an image, but half the image (down the y-axis) that has been rotated "out of" the page, disappears. How to fix? heading is in radians.
CALayer *layer = myUIImageView.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, heading, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;

The solution to this was to set the zPosition property of all my layers appropriately. Thanks is due to #Brad Larson, who suggested this solution in a comment here. It seems that, when you start using CATransform3D, the normal zindex view hierarchy established by addsubview is thrown out the window.

layer.anchorPoint = CGPointMake(0.0, 0.0);

Setting the anchorPoint to {0.0, 0.0} works as well (as Liam already pointed out).
Here's the full code snippet for changing the anchorPoint without changing the layer's position on screen:
layer.anchorPoint = CGPointMake( 0.0, 0.0 );
CGPoint position = layer.position;
CGSize size = layer.bounds.size;
CGFloat posX = position.x - size.width / 2.0;
CGFloat posY = position.y - size.height / 2.0;
layer.position = CGPointMake( posX, posY );

Related

How to make image rotation and 3D transform simultaneously?

I am working on image editing application(resize, rotation, 3d transform). If I am rotate the image and then make 3D transform. Its going to initial image. Otherwise I am change the 3D transform then make rotation, Its also goes to the initial state.
For image 3D Transform I am using below code :
- (void)moveImage:(UIPanGestureRecognizer *)recognizer
{
if ([recognizer respondsToSelector:#selector(translationInView:)]) {
CGPoint translation = [(UIPanGestureRecognizer *)recognizer translationInView:recognizer.view.superview];
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500.0;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, - self.xTranslation / 50.0 * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
rotationAndPerspectiveTransform.m34 = - 1.0 / 500.0;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, - self.yTranslation / 50.0 * M_PI / 180.0f, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
}
}
For image Rotation I am using below code :
- (void)rotateImage:(UIRotationGestureRecognizer *)recognizer
{
if (state == RSImageViewStateResizing) {
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
recognizer.rotation = 0;
}
}
How to I fix this issue. Always welcome your suggestions, sample code, Sample project, any App store applications. Thanks in advance. Any one help me.
Use CAAnimationGroup to combine multiple animation into one
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.animations=[NSArray arrayWithObject:resizeAnimation, rotationAnimation, 3d transformAnimation];// add here your CAAnimation reference and CATransform3D reference
[layer addAnimation:theGroup forKey:#"animatePosition"];

CATransform3D not working properly

CATransform3D not working properly, I am using the below code. My problem is If I change the transform Its working fine. Once I release my finger and then make more transform the image goes to the Initial image. How to set the last transform? I am using PanGesture for the transform. Please give some Idea. Thanks in advance.
if ([recognizer respondsToSelector:#selector(translationInView:)]) {
CGPoint translation = [(UIPanGestureRecognizer *)recognizer translationInView:recognizer.view.superview];
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, translation.x * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, -translation.y * M_PI / 180.0f, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
}
CATransform3DIdentity means you resetting the transformation every time your view receives touch. Use CATransform3D rotationAndPerspectiveTransform = layer.transform; instead.

Rotate a UIImageView around a point off screen? (center of uiImageView

Hi I have a UIImageView which is part on, part off screen.
I want to rotate the view around a point off screen (the center of the view.)
How do I do this?
fullRotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotationAnimation.fromValue= [NSNumber numberWithFloat:0.0f];
fullRotationAnimation.toValue = [NSNumber numberWithFloat:2 * M_PI];
fullRotationAnimation.duration = 4;
fullRotationAnimation.repeatCount = 1;
[self->greyRings.layer addAnimation:fullRotationAnimation forKey:#"360"];
I'm using that code at the moment but it just spins it around the center of the screen.
Any ideas please?
Using block animation:
- (void)rotateImageView:(UIImageView *)imageView aroundPoint:(CGPoint)point byAngle:(CGFloat)angle {
CGFloat sinA = sin(angle);
CGFloat cosA = cos(angle);
CGFloat x = point.x;
CGFloat y = point.y;
CGAffineTransform transform = CGAffineTransformMake(cosA,sinA,-sinA,cosA,x-x*cosA+y*sinA,y-x*sinA-y*cosA);
[UIView animateWithDuration:4.0 animations:^{
imageView.transform = transform;
}];
}
Note that angle is in radians, so a full rotation is 2.0*M_PI.
Maybe you have to set anchorPoint of CALayer: see Specifying a Layer’s Geometry for more detail.
//(0,0) is the left-bottom of your layer bounds
self->greyRings.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
I think a negative anchorPoint should also work. So you'd better give your bounds and we can calculate for you.

perspective transform on uiview

Here's the basic code to get started with:
[UIView transitionWithView:self.view duration:2.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^ {
CALayer *layer1 = leftband.layer;
layer1.anchorPoint = CGPointMake(0, 0.5);
CATransform3D rotationAndPerspectiveTransform1 = CATransform3DIdentity;
rotationAndPerspectiveTransform1.m34 = 1.0 / -500;
rotationAndPerspectiveTransform1 = CATransform3DRotate(rotationAndPerspectiveTransform1, DegreesToRadians(-180.0f), 0.0f, 1.0f, 0.0f);
layer1.transform = rotationAndPerspectiveTransform1;
CALayer *layer2 = rightband.layer;
layer2.anchorPoint = CGPointMake(0, 0.5);
CATransform3D rotationAndPerspectiveTransform2 = CATransform3DIdentity;
rotationAndPerspectiveTransform2.m34 = 1.0 / -500;
rotationAndPerspectiveTransform2 = CATransform3DRotate(rotationAndPerspectiveTransform2, DegreesToRadians(180.0f), 0.0f, 1.0f, 0.0f);
layer2.transform = rotationAndPerspectiveTransform2;
}
completion:nil {
}];
leftband and rightband are two UIImageViews. The effect I want is that the leftband animates with its transform along the Y-axis towards the left direction. And the rightband does the same along its Y-axis towards the right.
But the above code results in both left and right imageviews animating in the same direction (it happens only towards left). I guess my understanding of anchor points is wrong. Both -180° and +180° in the rotation transform result in a leftward animation.
What am I doing wrong here?
Basically setting the anchor points before the animation block runs solves the issue. Anchor point for left -> (0.0f,0.5f) and for right -> (1.0f,0.5f).

iPhone Core Animation - Drawing a Circle

I wish to create an animation. The best way I can explain this is if you can imagine drawing a circle. It starts at the top or 12 o clock and draws clockwise all the way around until it becomes a complete circle over the space of 10 or so seconds.
The closet I have come to this is to draw a point rotating around a center point using Core Animation (using the sample code here). But I am at a loss on how to draw the circle? Any suggestions are very welcome.
Many Thanks :)
What you really should do is to animate the stroke of a CAShapeLayer where the path is a circle. This will be accelerated using Core Animation and is less messy then to draw part of a circle in -drawRect:.
The code below will create a circle shape layer in the center of the screen and animate the stroke of it clockwise so that it looks as if it is being drawn. You can of course use any shape you'd like. (You can read this article on Ole Begemanns blog to learn more about how to animate the stroke of a shape layer.)
Note: that the stoke properties are not the same as the border properties on the layer. To change the width of the stroke you should use "lineWidth" instead of "borderWitdh" etc.
// Set up the shape of the circle
int radius = 100;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
// Configure the apperence of the circle
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:circle];
// Configure animation
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 10.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Add the animation to the circle
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
Here is another solution to the problem, based off #David's answer. This approach lets you set the direction of the circle's animation, and offers slightly more control. Edit: I've written a blog post on how to draw a circle with Swift, which I'll try to keep up to date with the betas. Check there if the code below doesn't work for you.
let radius = 100.0
// Create the circle layer
var circle = CAShapeLayer()
// Set the center of the circle to be the center of the view
let center = CGPointMake(CGRectGetMidX(self.frame) - radius, CGRectGetMidY(self.frame) - radius)
let fractionOfCircle = 3.0 / 4.0
let twoPi = 2.0 * Double(M_PI)
// The starting angle is given by the fraction of the circle that the point is at, divided by 2 * Pi and less
// We subtract M_PI_2 to rotate the circle 90 degrees to make it more intuitive (i.e. like a clock face with zero at the top, 1/4 at RHS, 1/2 at bottom, etc.)
let startAngle = Double(fractionOfCircle) / Double(twoPi) - Double(M_PI_2)
let endAngle = 0.0 - Double(M_PI_2)
let clockwise: Bool = true
// `clockwise` tells the circle whether to animate in a clockwise or anti clockwise direction
circle.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: clockwise).CGPath
// Configure the circle
circle.fillColor = UIColor.blackColor().CGColor
circle.strokeColor = UIColor.redColor().CGColor
circle.lineWidth = 5
// When it gets to the end of its animation, leave it at 0% stroke filled
circle.strokeEnd = 0.0
// Add the circle to the parent layer
self.layer.addSublayer(circle)
// Configure the animation
var drawAnimation = CABasicAnimation(keyPath: "strokeEnd")
drawAnimation.repeatCount = 1.0
// Animate from the full stroke being drawn to none of the stroke being drawn
drawAnimation.fromValue = NSNumber(double: fractionOfCircle)
drawAnimation.toValue = NSNumber(float: 0.0)
drawAnimation.duration = 30.0
drawAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
// Add the animation to the circle
circle.addAnimation(drawAnimation, forKey: "drawCircleAnimation")
I got a solution to this problem, code below. I seem to have messed up in my explanation of the problem at hand so rewrote it as another question that got answered with the correct solution, see here
thanks everyone for their advice!
-(void)drawRect:(CGRect)rect
{
// Drawing code
CGRect allRect = self.bounds;
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw background
CGContextSetRGBStrokeColor(context, self.strokeValueRed, self.strokeValueGreen, self.strokeValueBlue, self.strokeValueAlpha); // white
CGContextSetLineWidth(context, 5);
// Draw progress
CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
CGFloat radius = (allRect.size.width - 4) / 2;
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
CGContextStrokePath(context);
}
Ok I have a partial fix. I found the following code that draws segments of a circle. Its basically a UIView subclass that gets its drawRect fired every time its progress should be updated. The only question I have now is how would I change it so that instead of drawing sections or slices it would just draw the circle stroke?
-(void)drawRect:(CGRect)rect
{
// Drawing code
CGRect allRect = self.bounds;
CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw background
CGContextSetRGBStrokeColor(context, self.strokeValueRed, self.strokeValueGreen, self.strokeValueBlue, self.strokeValueAlpha); // white
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 0.1f); // translucent white
CGContextSetLineWidth(context, self.lineWidth);
CGContextFillEllipseInRect(context, circleRect);
CGContextStrokeEllipseInRect(context, circleRect);
// Draw progress
CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
CGFloat radius = (allRect.size.width - 4) / 2;
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white
CGContextMoveToPoint(context, center.x, center.y);
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
CGContextClosePath(context);
CGContextFillPath(context);
}