Note: Source code (DrawingText) available on Github

In the last article, we covered the basics of bitmap drawing with Swift and the Quartz/Core Image APIs. As I was playing with that, I wondered how text could be drawn on a bitmap. In Flash, the BitmapData API does not have an API for drawing text, the workaround was to use a TextField offscreen that would be passed to the BitmapData.draw API.

On iOS, there is also no API to directly do that on an image, the idea is to rely on the graphics context, draw our initial image on the graphics context, then draw the text on the context too, then grab the resulted composited image using our best friend UIGraphicsGetImageFromCurrentImageContext.

Here is below what we are going to produce as a result:

copyright

As you can see, we will add at the bottom right of the image a small copyright message, composited onto the image.

This idea was brought up on this thread on Stackoverflow, so here we go with the Swift code to achieve this:

import SpriteKit

class GameScene: SKScene {
    override func didMoveToView(view: SKView) {

        // we pick the font we want to use
        let font = UIFont(name: "Arial", size: 18)

        // our string, note that we use here NSString instead of String that has more APIs like drawInRect and size
        // it is preferred to use Swift native rypes like String but for now String has a limited API surface
        let text: NSString = "Copyright © - Thibault Imbert"

        // we reference our image (path)
        let data = NSData (contentsOfFile: "/Users/timbert/Documents/Ayden.jpg")

        // we create a UIImage out of it
        let image = UIImage(data: data)

        // our rectangle for the drawing size
        let rect = CGRectMake(0, 0, image.size.width, image.size.height)

        // we create our graphics context at the size of our image
        UIGraphicsBeginImageContextWithOptions(CGSize(width: rect.width, height: rect.height), true, 0)

        // we retrieve it
        let context = UIGraphicsGetCurrentContext()

        // we set our color to white (this will be the text color)
        CGContextSetFillColorWithColor(context, CGColorCreateGenericRGB(1, 1, 1, 1))

        // we draw our image to the graphics context
        image.drawInRect(rect)

        // a dictionary informing about the font used, required by sizeWithAttributes to query the text size
        let attr = [NSFontAttributeName: font]

        // the size of our text
        let size = text.sizeWithAttributes(attr)

        // the rect for the drawing position of our copyright text message
        let rectText = CGRectMake(image.size.width-size.width, image.size.height-(size.height+4), image.size.width-(size.width+4), image.size.height)

        // we draw the text on the graphics context, with our font
        text.drawInRect(rectText, withFont: font)

        // we grab a UIImage from the graphics context
        let newImage = UIGraphicsGetImageFromCurrentImageContext();

        // we remove our bitmap from the stack
        UIGraphicsEndImageContext();

        // we create a texture, pass the UIImage
        var texture = SKTexture(image: newImage)

        // wrap it inside a sprite node
        var sprite = SKSpriteNode(texture:texture)

        // we scale it a bit
        sprite.setScale(0.5);

        // we position it
        sprite.position = CGPoint (x: 510, y: 300)

        // let's display it
        self.addChild(sprite)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }
}

Note that in this example, we are relying on the NSString object, which is not a Swift native type. For now, the String object in Swift has a pretty limited API surface compared to the NSString object, with convenient APIs like sizeInFont or drawInRect. Hopefully the native String type in Swift will be better equipped in the future.