iOS: How Can I Rotate A CGLayer?
-
18-06-2021 - |
Domanda
I have been looking far and wide for the answer. I'm still coming up to speed on iOS dev, so I can definitely accept that I may be clooless here. However, the complete and utter lack of answers out there makes me wonder if there actually is an answer.
Here's the deal: I am using a CGLayer to generate a fairly basic vector polygon:
- (CGLayerRef)getASegment
{
CGSize layerSize = s_viewRects[kASegment].size;
CGLayerRef theLayer = CGLayerCreateWithContext ( UIGraphicsGetCurrentContext(),
layerSize, nil );
CGContextRef context = CGLayerGetContext ( theLayer );
CGContextAddLines ( context, s_shapePoints, 7 );
return theLayer;
}
s_viewRects is a static array that holds the container rects, as placed in the overall image, and s_shapePoints contains an array of CGPoint structs that maps out a distorted hexagon.
Now, I want to re-use this shape. It needs to be flipped and rotated in order to fit all the uses (for the record, these are "segments" in an "LED" display).
I get the shape fine. No problem.
The problem is in rotating it for re-use. There doesn't seem to be any way to actually rotate a CGLayer. Lots of ways to rotate CALayers, but not CGLayers. I'd be happy to use a CALayer, except that CALayers seem to be all focused on raster graphics, and I need vector graphics.
I'm sure there must be an obvious way to address this. The only way that I have seen, so far, is to rotate the entire target graphics context, draw the shape, then un-rotate the context. The equivalent of turning the house to unscrew a lightbulb.
The only way that I can get it to work, is to actually transform the points before drawing each segment separately, which means no re-use. Doing a CGContextRotateCTM during the creation of the shape does not actually result in a rotated element when displayed.
Any clues?
Soluzione
The only way that I have seen, so far, is to rotate the entire target graphics context, draw the shape, then un-rotate the context. The equivalent of turning the house to unscrew a lightbulb.
I'm pretty certain that is your solution. It's how drawing with CGContextRef
works.
I'd be happy to use a CALayer, except that CALayers seem to be all focused on raster graphics, and I need vector graphics.
Have you tried using a CAShapeLayer
?
Altri suggerimenti
OK, as promised, here is the code.
First, I use a CAShapeLayer, and create it in a manner similar to above:
- (CALayer *)getSegment
{
CGRect layerFrame = CGRectMake ( 0, 0, s_viewRects[kASegment].size.width, s_viewRects[kASegment].size.height );
CAShapeLayer *ret = [CAShapeLayer layer];
[ret setFrame:layerFrame];
CGMutablePathRef thePath = CGPathCreateMutable ();
CGPathAddLines ( thePath, nil, s_shapePoints, 7 );
[ret setPath:thePath];
return ret;
}
Then, I set it into a main layer, like so, doing the transform along the way:
_top_left_element = [self getSegment];
frame = [self calculateRectForElement:_top_left_element];
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DRotate(transform, M_PI_2, 0.0, 0.0, -1.0 );
[_top_left_element setTransform:transform];
[_top_left_element setFrame:frame];
[_main_element addSublayer:_top_left_element];
The CATransform3DRotate is necessary, as the transform property of the layer is a 3D one. I use M_PI_2, as all transforms are in Radians (M_PI_2 is a convenient macro that means "90°"), and the -1 will make the rotate go counter-clockwise.
calculateRectForElement is a function that figures out where everything is placed. It simply returns a rect that positions the element.
_top_left_element is a class property that is a CALayer*.
I did this, and Bjorn Strongintheram's your uncle.
UPDATE: I wanted to add that you need to make sure that you CGPathRelease the layer's paths in the unload/destruct, or you will get a leak.