iOS Drawing text in the centre of shapes
Asked Answered
A

4

10

I'm trying to draw text in the centre of shapes in iOS, an example would be Microsoft Office's insert shape see: https://www.dropbox.com/s/cgqyyuvy6hcv5g8/Screenshot%202014-01-21%2013.48.17.png

I have an array of coordinates that are used to form the shape, which works fine for creating the actual shape.

My first tactic was following this stackoverflow question: Core Text With Asymmetric Shape on iOS however I can only get it to draw the text at the bottom of the shape. Is there any way to centre the text vertically and horizontally?

I then had a look at the new TextKit but I got stuck with how to subclass NSTextContainer to accept either a CGPath or an array of coordinates to create the text container to draw inside.

My backup solution is to use Core Text to draw the text at the centre coordinate of a shape, however this will cause the text to draw outside of the shape on some shapes such as a right angled triangle.

Alternatively, are there any other methods I could use to get some text somewhat in the centre of a shape?

Artemas answered 21/1, 2014 at 14:2 Comment(0)
M
21

I cherrypicked from these answers and from related questions - none of them were truly satisfying - and came up with this simple solution:

    UIGraphicsBeginImageContext(CGSize.init(width: imageWidth, height: imageHeight))

    let string = "ABC" as NSString

    let attributes = [
            NSFontAttributeName : UIFont.systemFontOfSize(40),
            NSForegroundColorAttributeName : UIColor.redColor()
        ]

    // Get the width and height that the text will occupy.
    let stringSize = string.sizeWithAttributes(attributes)

    // Center a rect inside of the image
    // by going half the difference to the right and down.
    string.drawInRect(
        CGRectMake(
            (imageWidth - stringSize.width) / 2,
            (imageHeight - stringSize.height) / 2,
            stringSize.width,
            stringSize.height
        ),
        withAttributes: attributes
    )

    let newImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

The result (nevermind the colours):

String ABC centered in image

Moue answered 23/2, 2016 at 9:54 Comment(0)
F
7

Swift 3 Version of Luca Davanzo's answer

struct UIUtility {

static func imageWithColor(color: UIColor, circular: Bool) -> UIImage? {
    let size: CGFloat = circular ? 100 : 1;
    let rect = CGRect(x: 0.0, y: 0.0, width: size, height: size)
    UIGraphicsBeginImageContext(rect.size)
    if let context = UIGraphicsGetCurrentContext() {
        context.setFillColor(color.cgColor)
        if(circular) {
            context.fillEllipse(in: rect)
        }
        else {
            context.fill(rect)
        }
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
    return nil
}

static func drawText(text: NSString, font: UIFont, image: UIImage, point: CGPoint, textColor: UIColor = UIColor.white) -> UIImage? {
    UIGraphicsBeginImageContext(image.size)
    image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
    let rect = CGRect(x: point.x, y: point.y, width: image.size.width, height: image.size.height)
    text.draw(in: rect.integral, withAttributes: [ NSFontAttributeName: font, NSForegroundColorAttributeName: textColor ])
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

static func circleImageWithText(text: String, font: UIFont, circleColor: UIColor, textColor: UIColor = UIColor.white) -> UIImage? {
    if let image = UIUtility.imageWithColor(color: circleColor, circular: true) {
        let textSize = NSString(string: text).size(attributes: [ NSFontAttributeName: font])
        let centerX = ((image.size.width) / 2.0) - (textSize.width / 2.0)
        let centerY = ((image.size.height) / 2.0) - (textSize.height / 2.0)
        let middlePoint = CGPoint(x: centerX, y: centerY)
        return UIUtility.drawText(text: text as NSString, font: font, image: image, point: middlePoint, textColor: textColor)
    }
    return nil
}
}
Fruition answered 29/3, 2017 at 11:10 Comment(0)
K
4

That's a bit tricky. First you have to create a context. (bounds.size is the size of the image)

UIGraphicsBeginImageContext(bounds.size); 
CGContextRef context = UIGraphicsGetCurrentContext();

Then get the text's bounding box: (font is the font you want to use)

CGSize textSize =
[yourText sizeWithAttributes:@{NSFontAttributeName:font}];

draw your text into the context (x and y marks the center of the image)

[yourText drawAtPoint:CGPointMake(x, y) withAttributes:@{NSFontAttributeName:font}];

Get the image

UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 

End image context

UIGraphicsEndImageContext();

I haven't tested it, but it's the way to go.

Kaifeng answered 21/1, 2014 at 14:30 Comment(3)
Thanks for the idea! - will this work if I'm drawing multiple shapes with text in the middle in drawRect in a single view though?Artemas
Sure ut will. You can handle each drawing separately, if you use cgcontextsavecgstate and cgcontextrestorecgstateKaifeng
sorry, I think I am missing something. Why did u get the bounding box in this example?Airway
T
4

Swift 2.0 compatible.

My purpose was draw a circle shape with centered text inside.

struct UIUtility {

  static func imageWithColor(color: UIColor, circular : Bool) -> UIImage {
    let size : CGFloat = circular ? 100 : 1;
    let rect = CGRectMake(0.0, 0.0, size, size)
    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()
    CGContextSetFillColorWithColor(context, color.CGColor)
    if(circular) {
        CGContextFillEllipseInRect(context, rect)
    }
    else {
        CGContextFillRect(context, rect)
    }
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
  }

  static func drawText(text: NSString, font: UIFont, image: UIImage, point: CGPoint, textColor: UIColor = UIColor.whiteColor()) -> UIImage {
    UIGraphicsBeginImageContext(image.size)
    image.drawInRect(CGRectMake(0, 0, image.size.width, image.size.height))
    let rect = CGRectMake(point.x, point.y, image.size.width, image.size.height)
    text.drawInRect(CGRectIntegral(rect), withAttributes: [ NSFontAttributeName: font, NSForegroundColorAttributeName : textColor ])
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
  }

  static func circleImageWithText(text text: String, font: UIFont, circleColor: UIColor, textColor: UIColor = UIColor.whiteColor()) -> UIImage {
    let image = UIUtility.imageWithColor(circleColor, circular: true)
    let textSize = NSString(string: text).sizeWithAttributes([ NSFontAttributeName: font])
    let centerX = (image.size.width / 2.0) - (textSize.width / 2.0)
    let centerY = (image.size.height / 2.0) - (textSize.height / 2.0)
    let middlePoint = CGPointMake(centerX, centerY)
    return UIUtility.drawText(text, font: font, image: image, point: middlePoint)
  }

}

With this utility we can easy create an image in this way:

let font = UIFont()
let image = UIUtility.circleImageWithText(text: "Center text", font: font, circleColor: UIColor.blackColor())
Tiger answered 22/10, 2015 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.