Reputation: 41
I am new to Cocoa/Swift applications for Mac OS 10.11 (Not iOS). I created an NSView
with the size of A4
paper, drew some simple lines and some text using NSString.drawAtPoint
within drawRect()
. So far it works.
However, from time to time, I need a few characters to be turned 90 degrees, and some text needs to be scaled at X, or Y direction. That is where I don't know how to do it. Please can someone tell me how to achieve these?
Many thanks.
Upvotes: 0
Views: 1713
Reputation: 11
Here is similar answer using Swift 3.1 for cocoa / macOS. Some convenience methods have been added to the graphic context in order to apply rotation and other common transformations, without having to create a separate AffineTransform.
The real trick to remember here, is that when you rotate the context, you are rotating the entire coordinate system, so you have to translate the point at which you want to draw the text in the non-rotated context, to an overlapping point in the rotated context.
func drawVerticalText(text: String, withAttributes attributes: [String : Any]?, origin: CGPoint, context: CGContext) {
// Draws text that rotated 90 degrees ( pi/2 radians ) counterclockwise.
/*
Rotate entire drawing context 90 degrees clockwise including axis orientation!
i.e. the positive Y axis is pointing to the left instead of up, and positive X axis
is pointing up instead of to the right. This also means that anything drawn having
two positive x and y coordinates will be rendered off screen of the current view.
*/
let halfRadian : CGFloat = CGFloat.pi / 2.0
context.rotate(by: halfRadian )
/*
In order to have the text rendered in our current view, we have to find a point in our
rotated context, that overlays the point where we want to draw the text in our non-rotated
context. The x-axis is horizontal in the non-rotated context (ascending values to the
right), while the y-axis is horizontal in our rotated context (ascending values to the
left). So our oppisite value of our x coordinate in the non-rotated context must be
mapped to the y-axis in our rotated context. Accordingly, the y-axis is vertical in our
non-rotated context (ascending values upwards), while x-axis is vertical in our rotated
context (ascending values upwards). So our y value in the non-rotated context must be
mapped to the x-axis in our rotated context. i.e. If we graph a point (Y,-X) in our rotated
context, it will overlap the point (X,Y) in our non-rotated context.
*/
// The origin represents the lower left corner of the rectangle in which the text will be rendered.
let translatedOrigin = NSPoint(x: origin.y, y: -origin.x)
// Draw the text.
text.draw(at: translatedOrigin, withAttributes: attributes)
// Rotate the context counter-clockwise 90 degrees ( pi/2 radians ) after we are done.
context.rotate(by: -halfRadian )
}
Here is what the call would look like in your NSView:
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let context = NSGraphicsContext.current()?.cgContext
drawVerticalText(text: "My text string", withAttributes: nil, origin: CGPoint(x: 50.0, y: 100.0) , context: context!)
}
Upvotes: 1
Reputation: 16987
You can use NSAffineTransform
. This code draws a capital "A" rotated 90 degrees clockwise:
[NSGraphicsContext saveGraphicsContext]; // save current affine transform
[[[NSAffineTransform transform] rotateByDegrees:90] concat];
[@"A" drawAtPoint:point withAttributes:@{}];
[NSGraphicsContext restoreGraphicsContext]; // restore original transform
Note that rotateByDegrees
rotates the graphics context around the origin point. If you want to rotate around some other point, you need to add a couple of translations. This concat
s a transform that rotates around rotatePoint
:
NSAffineTransform *transform = [NSAffineTransform transform];
[transform translateXBy:rotatePoint.x yBy:rotatePoint.y];
[transform rotateByDegrees:90];
[transform translateXBy:-rotatePoint.x yBy:-rotatePoint.y];
[transform contat];
Upvotes: 4