I've a rectangle shaped CALayer, which I've scaled as below:
CATransform3D currentTransform = layer.transform;
CATransform3D newTransform = CATransform3DScale(currentTransform, xscale, yscale, 1.0);
[layer setTransform:newTransform];
After applying CATransform3DScale, transformed CALayer is also a rectangle.
But when I apply CATransform3DRotate, transformed CALayer becomes a rhombus.
CATransform3D currentTransform = layer.transform;
CATransform3D newTransform = CATransform3DRotate(currentTransform, rotation, 0.0, 0.0, 1.0);
[layer setTransform:newTransform];
How can I get the rectangle shape after applying rotate transform?
I didn't change anchor point of the layer. Sometimes I need to change the position of the layer.
Related
I need a radial gradient in the shape of an oval or ellipse and it seems like it CGContextDrawRadialGradient can only draw a perfect circle. I've been drawing to a square context then copying/drawing into a rectangular context.
Any better way to do this?
Thanks!
The only way I've found to do this is as Mark F suggested, but I think the answer needs an example to be easier to understand.
Draw an elliptical gradient in a view in iOS (and using ARC):
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
// Create gradient
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
UIColor *centerColor = [UIColor orangeColor];
UIColor *edgeColor = [UIColor purpleColor];
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)centerColor.CGColor, (__bridge id)edgeColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
// Scaling transformation and keeping track of the inverse
CGAffineTransform scaleT = CGAffineTransformMakeScale(2, 1.0);
CGAffineTransform invScaleT = CGAffineTransformInvert(scaleT);
// Extract the Sx and Sy elements from the inverse matrix
// (See the Quartz documentation for the math behind the matrices)
CGPoint invS = CGPointMake(invScaleT.a, invScaleT.d);
// Transform center and radius of gradient with the inverse
CGPoint center = CGPointMake((self.bounds.size.width / 2) * invS.x, (self.bounds.size.height / 2) * invS.y);
CGFloat radius = (self.bounds.size.width / 2) * invS.x;
// Draw the gradient with the scale transform on the context
CGContextScaleCTM(ctx, scaleT.a, scaleT.d);
CGContextDrawRadialGradient(ctx, gradient, center, 0, center, radius, kCGGradientDrawsBeforeStartLocation);
// Reset the context
CGContextScaleCTM(ctx, invS.x, invS.y);
// Continue to draw whatever else ...
// Clean up the memory used by Quartz
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
Put in a view with a black background you get:
You can change the transform of the context to draw an ellipse (for example, apply CGContextScaleCTM(context, 2.0, 1.0) just before calling CGContextDrawRadialGradient () to draw an elliptical gradient that's twice as wide as it is high). Just remember to apply the inverse transform to your start and end points, though.
I have very simple task, but I can't understand CATransform3DRotate.
I have 3 UIImageView with equal height and width, and I need set some transformation for left and right items for next result:
https://dl.dropboxusercontent.com/u/15196617/result.png
Original screen:
https://dl.dropboxusercontent.com/u/15196617/original.png
My sources:
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.f / self.prevItemImageView.bounds.size.width;
transform = CATransform3DRotate(transform, (CGFloat) -(M_PI / 3.f), 0, 1.f, 0);
self.prevItemImageView.layer.anchorPoint = CGPointMake(1.f, 0.5f);
self.prevItemImageView.layer.transform = transform;
transform = CATransform3DIdentity;
transform.m34 = -1.f / self.nextItemImageView.bounds.size.width;
transform = CATransform3DRotate(transform, (CGFloat) (M_PI / 3.f), 0, 0.5f, 0);
self.nextItemImageView.layer.anchorPoint = CGPointMake(0.f, 0.5f);
self.nextItemImageView.layer.transform = transform;
It is not work.
What you want to do is apply the transform with altered .m34 component to the superlayer of your image views, as a sublayerTransform. That is what gives transforms on the image views the appearance of depth.
This image started life as a square. But it looks rotated in 3D because its superlayer has a sublayerTransform:
I am drawing polygon using CGPath and adding to CAShapeLayer.I want to scale my CGPath when user click on it. I know how to scale CGPath. But when i click my CGPath, my CGPath drawing far from centre while i am drawing polygon in centre.
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
CGPathRef oldPath = polygonLayer.path;
CGPathRef scaledPath = CGPathCreateCopyByTransformingPath(oldPath, &scaleTransform);
polygonLayer.path = scaledPath;
The problem is that you're used to UIView transformation, which is done from the center of the view.
CGPath transformation is done on the points (imagine CGPointZero as the center of the path).
My solution: translate to CGPointZero , scale , and then back to your original coordinates.
CGPathRef CGPath_NGCreateCopyByScalingPathAroundCentre(CGPathRef path,
const float scale)
{
CGRect bounding = CGPathGetPathBoundingBox(path);
CGPoint pathCenterPoint = CGPointMake(CGRectGetMidX(bounding), CGRectGetMidY(bounding));
CGAffineTransform translateAndScale = CGAffineTransformTranslate( CGAffineTransformMakeScale(scale, scale), - pathCenterPoint.x, -pathCenterPoint.y) ;
CGAffineTransform translateBack = CGAffineTransformMakeTranslation(pathCenterPoint.x, pathCenterPoint.y);
CGPathRef centeredAndScaled = CGPathCreateCopyByTransformingPath(path, &translateAndScale);
CGPathRef translatedPathRef = CGPathCreateCopyByTransformingPath(centeredAndScaled, &translateBack);
CGPathRelease(centeredAndScaled);
return translatedPathRef;
}
Basically, I'm rotating a layer about a point as :
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -500;
transform = CATransform3DTranslate(transform, rotationPoint.x-center.x, rotationPoint.y-center.y, 0.0);
transform = CATransform3DRotate(transform, (rotationAngleFor) * M_PI / 180.0f, 0.0, 1.0, 0);
transform = CATransform3DTranslate(transform, center.x-rotationPoint.x, center.y-rotationPoint.y, 0.0);
I also create a layer, add it to a bigger layer and then apply the transform to it:
[self.layer addSublayer:myLayer];
myLayer.transform = transform;
How to animate this?
Note- Putting this in a UIView animation block doesn't work.
Edit: As #DuncanC pointed out my description did not match the actual code.
You can use a CABasicAnimation that is added to the layer as follows.
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath: #"transform"];
<here goes your definition of transform>
transformAnimation.toValue = [NSValue valueWithCATransform3D:newTransform];
transformAnimation.duration = 10;
[self.layer addAnimation:transformAnimation forKey:#"transform"];
This animation performs a continuous change of the transform property of the layer from the original value of the transform property of the layer to the newTransform transformation. The change takes 10 seconds.
I am trying to get the pitch from CMMotion's attitude and make AVCaptureVideoPreviewLayer transform with pitch angle using CATransform3D. Something like this:
CATransform3D transform = CATransform3DIdentity;
float zDistance = 850;
transform.m34 = 1.0 / -zDistance;
transform = CATransform3DRotate(transform, -_pitch, 1.0f, 0.0f, 0.0f);
transform = CATransform3DScale(transform, 0.5, 0.5, 0.0);
[_videoPreviewLayer setTransform:transform];
However, the transformed AVCaptureVideoPreviewLayer is so slow and it takes around 10 seconds to be transformed. Is there any suggestion to improve the preview layer with 3D transformation more smooth?
Thanks a lot!