Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw String in CGContext?

I am trying to draw a string in the overriden draw method of CALayer (I'm programming for iOS).

override func draw(in ctx: CGContext) {
    let font = UIFont.systemFont(ofSize: 30)
    let string = NSAttributedString(string: "23", attributes: [NSAttributedStringKey.font: font])
    string.draw(at: CGPoint(x: 200, y: 200))
}

This is however not drawing anything (at least nothing is visible). Changing fill and stroke color does not make a difference.

If I draw a line it will show, so the function is being called. I know there is a CATextLayer but I need to draw the string directly. How are you supposed to draw a string in CGContext in the Swift 4 era? No amount of net searching has yielded an answer.

like image 385
Melodius Avatar asked Oct 18 '18 11:10

Melodius


2 Answers

I assume you know all other settings. The key here is you have not make the CGContext as current one. Just add two lines code to solve the problem. Hope you get the answer.

   override func draw(in ctx: CGContext) {
   UIGraphicsPushContext(ctx)
    let font = UIFont.systemFont(ofSize: 30)
    let string = NSAttributedString(string: "23", attributes: [NSAttributedString.Key.font: font])
    string.draw(at: CGPoint(x: 200, y: 200))
   UIGraphicsPopContext()
}
like image 193
E.Coms Avatar answered Oct 12 '22 23:10

E.Coms


The above answer works if a view is focused.

Unfortunately, it won't work in an offscreen Bitmap or CALayer context.

The right and universal way to draw a string in any CGContext is to use the CoreText api. It will work on all Apple platforms, and has a lot of power under the hood.

https://developer.apple.com/documentation/coretext

Example:

import Foundation
import Quartz

/// LazyFoxLayer
///
/// Will draw the "The lazy fox…" string in the bottom right corner of the layer,
/// with a margin of 10 pixels.

class LazyFoxLayer: CALayer {

    func draw(ctx: CGContext) {
        ctx.saveGState()
    
        // Parameters

        let margin: CGFloat = 10
        let color = CGColor.black
        let fontSize: CGFloat = 32
        // You can use the Font Book app to find the name
        let fontName = "Chalkboard" as CFString 
        let font = CTFontCreateWithName(fontName, fontSize, nil)

        let attributes: [NSAttributedString.Key : Any] = [.font: font, .foregroundColor: color]

        // Text

        let string = "The lazy fox…"
        let attributedString = NSAttributedString(string: string, 
                                                  attributes: attributes)

        // Render

        let line = CTLineCreateWithAttributedString(attributedString)
        let stringRect = CTLineGetImageBounds(line, ctx)

        ctx.textPosition = CGPoint(x: bounds.maxX - stringRect.width - margin, 
                                   y: bounds.minY + margin)

        CTLineDraw(line, ctx)

        ctx.restoreGState()
    }
}

Cheers :)

like image 6
Moose Avatar answered Oct 12 '22 22:10

Moose