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()
}
The context provides the information on where to draw: "You can think of a graphics context as a drawing destination" (Apple Developer).

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:


In Swift we could add the following code to the drawRect() method in order to draw the circle:
// 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
Endorse on Coderwall


visit link download