How to rotate CGContext about center?

2.5k views Asked by At

I have the following code to create a multi-colored ImageContext:

UIGraphicsBeginImageContext(self.view.frame.size);
[self.imageToDelete drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);

CGColorRef mycolor;

mycolor = [[UIColor blueColor] CGColor];

CGPoint mid = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);

CGContextMoveToPoint(UIGraphicsGetCurrentContext(), mid.x, mid.y);


CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), mid.x, mid.y+50);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(),3);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), mycolor);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());

mycolor = [[UIColor whiteColor] CGColor];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), mid.x, mid.y+50);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), mid.x, mid.y+75);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(),3);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), mycolor);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());

mycolor = [[UIColor cyanColor] CGColor];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), mid.x, mid.y+75);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), mid.x, mid.y+100);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(),3);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), mycolor);

CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());

self.imageToDelete = UIGraphicsGetImageFromCurrentImageContext();

[self.mainImage drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeCopy alpha:1.0];
[self.imageToDelete drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1];
self.mainImage = UIGraphicsGetImageFromCurrentImageContext();

SKSpriteNode *node = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImage:self.mainImage]];
node.zPosition = 100;
node.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
node.size = self.mainImage.size;
[self addChild:node];

UIGraphicsEndImageContext();

This is the result:

result

I would like to rotate this line about the center of the screen (or the top of this line), and keep the original line as well. Is there a way to make a copy, and rotate it about the center point? To clarify, mid is a CGPoint defined as (self.view.frame.size.width/2,self.view.frame.size.height/2), and this is done in Sprite-Kit, iOS, Objective-C.

2

There are 2 answers

1
matt On

Rotation is around the origin. To rotate around another point, translate that point to the origin, rotate, and translate back.

0
clearlight On

This supplements @matt's answer with an example (with Swift 5, iOS 16), ... a function added to a subclass of UIView, where the view is comprised of sublayers of CAShapeLayer derived from UIBezierPath.

As Matt indicated, CGContext rotation centers at origin, therefore one must translate center of view to origin, do the rotation, then translate back. Note, also the conversion of degrees to radians in the parameter of rotate().

func asImage(rotationDegrees: CGFloat) -> UIImage {
    let renderer = UIGraphicsImageRenderer(bounds: bounds)
    let image = renderer.image(actions: { _ in
        if let ctx = UIGraphicsGetCurrentContext() {
            ctx.translateBy(x: bounds.size.width / 2, y: bounds.size.height / 2)
            ctx.rotate(by: rotationDegrees * .pi / 180)
            ctx.translateBy(x: -bounds.size.width / 2, y: -bounds.size.height / 2)

            for sublayer in self.layer.sublayers!.reversed() {
                sublayer.render(in: ctx)
            }
        }
    })
    return image
}