Sunday, July 22, 2018
Swift Translating and Rotating a CGContext A Visual Explanation iOS Xcode
Swift Translating and Rotating a CGContext A Visual Explanation iOS Xcode
The purpose of this post is to demystify as far as possible the process of translating and rotating a CGContext.
Obtaining a reference to a CGContext
Before anything else, it is necessary to obtain a reference to the current context from the drawRect() method of a UIView subclass.
override func drawRect(rect:CGRect)
{
// obtain context
let ctx = UIGraphicsGetCurrentContext()
}
Drawing a circle
With the context available to us, lets suppose we draw a circle at the centre of it. The black square represents the context:// Decide on radius
let rad = CGRectGetWidth(rect)/3.5
// End angle will be 2*pi for any circle that begins at 0
let endAngle = CGFloat(2*M_PI)
// We could use CGContextAddEllipseInRect to draw a circle instead
CGContextAddArc(ctx, CGRectGetMidX(rect), CGRectGetMidY(rect), rad, 0, endAngle, 1)
// set stroke color
CGContextSetStrokeColorWithColor(ctx,UIColor.whiteColor().CGColor)
// Set line width
CGContextSetLineWidth(ctx, 4.0)
CGContextStrokePath(ctx)
Note: The code could be simpler, but I want to think here in terms of origins rather than rectangles and so for consistency Ive used the CGContextAddArc() method.
Saving the context state
Before any transformation takes place it is important to save the current context state, so that it can be returned to.
CGContextSaveGState(ctx)
Translating the origin of the context
Now the origin of the context can be translated to the origin of the circle like so:
In code this translation is the equivalent to writing
CGContextTranslateCTM(ctx, CGRectGetMidX(rect), CGRectGetMidY(rect))Note: the circle doesnt move with the context, all that has already been drawn stays where it is.
Rotating the context
Now the context has been translated to a new position it can also be rotated. The rotation happens around the origin of the context.In Swift the rotation would look like this:
CGContextRotateCTM(ctx, CGFloat(M_PI*45/180))
Again the pre-exisiting drawing does not change.
Drawing on the translated and rotated context
Lets suppose we now draw a line from (0, 0) to (radius, 0) where "radius" is the radius of the circle.
The Swift code looks like this:
// create a mutable path
let path = CGPathCreateMutable()
// move to the starting point of the path
CGPathMoveToPoint(path, nil, 0, 0)
// add a line the length of the radius to path
CGPathAddLineToPoint(path, nil, rad, 0)
// add the path to the (translated and rotated) context
CGContextAddPath(ctx, path)
// set line width
CGContextSetLineWidth(ctx, 4)
// set line color
CGContextSetStrokeColorWithColor(ctx,UIColor.whiteColor().CGColor)
// stroke path
CGContextStrokePath(ctx)
Restoring the context
Restoration of the context in code is as simple as the saving of it.CGContextRestoreGState(ctx)Once the context is restored back to its original state, this is how things look:
The new line stays exactly where it was drawn and any new drawing uses as its origin the original (0, 0) of the context.
Conclusion
Im not going to discuss pros and cons of shifting the graphics context around, although Apple does state: "The advantage of modifying the graphics context (as opposed to the path object itself) is that you can easily undo the transformation by saving and restoring the graphics state."
Here I simply wanted to explain what happens and to lay some foundations for a future post. Full code can be found here for you to copy and paste into an initial view controller.
Follow @sketchytech