t4ncr3d3 t4ncr3d3 - 1 year ago 120
iOS Question

ios swift core graphics - string is upside down

When I want to draw a String in a CGContext, the text appears upside down.

I know this subject has already been posted here, but none of the solution are working for me.

I do flip the context:

graphContext.scaleBy(x: 1.0, y: -1.0)
graphContext.translateBy(
x: CGFloat(0.0),
y: CGFloat(-1.0 * graphSize.h))


I tried to do the same with the textMatrix too, but this has no effect at all on the result.

Full project sources are here: dropbox
Screen shot here: screen shot

Can't figure why it is not working.

Thanks

My draw method:

override func draw(_ rect: CGRect) {

let graphSize = GSize(
w: Double(self.frame.width),
h: Double(self.frame.height))

rebaseAreas(graphSize: graphSize)

graphContext = UIGraphicsGetCurrentContext()!
graphContext.saveGState()

// Flip the context coordinates, in iOS only
graphContext.scaleBy(x: 1.0, y: -1.0)
graphContext.translateBy(
x: CGFloat(0.0),
y: CGFloat(-1.0 * graphSize.h))


// this has no effect, I can delete it and nothing changes
graphContext.textMatrix = CGAffineTransform(scaleX: 2.0, y: -2.0)


drawBackground(
inRect: graph_drawing_rect,
inContext: graphContext)


drawGuidelines(
vAxis: vAxis,
hAxis: hAxis,
inRect: serie_drawing_rect,
inContext: graphContext)


drawSerie(
points,
inRect: serie_drawing_rect,
inContext: graphContext)


drawTitle(
inRect: graph_drawing_rect,
inContext: graphContext)


drawVAxisLabels(
vAxis: vAxis,
inRect: x_labels_drawing_rect,
inContext: graphContext)


drawHAxisLabels(
hAxis: hAxis,
inRect: y_labels_drawing_rect,
inContext: graphContext)

graphContext.restoreGState()
}


And my drawTitle method:

func drawTitle(
inRect rect: GRect,
inContext context: CGContext) {

// saving the calling state
context.saveGState()

let title = titleLabelText

let lblAttributes = [
NSFontAttributeName: titleFont,
NSForegroundColorAttributeName: title_font_color,
] as [String : Any]

let origin = CGPoint(
x: title_label_position.x * rect.w,
y: title_label_position.y * rect.h)

let drawRect = CGRect(
origin: origin,
size: title.size(attributes: lblAttributes))

title.draw(
in: drawRect,
withAttributes: lblAttributes)

// returning the calling state
graphContext.restoreGState()
}

Answer Source

You're using UIKit's string-drawing support. UIKit adds the method you're using, drawInRect:withAttributes:, to NSString.

UIKit sets the text matrix when it draws the string. (You can set a breakpoint on CGContextSetTextMatrix to verify this.) This means that your attempt to set textMatrix has no effect.

UIKit expects the graphics context's origin to be at the top left of the drawing area, and it expects y coordinates to increase toward the bottom of the drawing area. You have flipped the direction of the y axis, so UIKit ends up drawing the text upside down.

Since you can't fix this using the text matrix, you have to fix it using the transform matrix. You need to translate the origin to the top-left corner of the rectangle that should contain the text, flip the y axis, draw the text at the origin, and then restore the transform matrix.

You can wrap this in an extension method on NSString:

private extension String {

    func drawFlipped(in rect: CGRect, withAttributes attributes: [String: Any]) {
        guard let gc = UIGraphicsGetCurrentContext() else { return }
        gc.saveGState()
        defer { gc.restoreGState() }
        gc.translateBy(x: rect.origin.x, y: rect.origin.y + rect.size.height)
        gc.scaleBy(x: 1, y: -1)
        self.draw(in: CGRect(origin: .zero, size: rect.size), withAttributes: attributes)
    }

}

Then you can use your extension method instead of draw(in:withAttributes:). For example, here's a replacement for your drawTitle(inRect:inContext:) method:

func drawTitle(
    inRect rect: GRect,
    inContext context: CGContext) {

    let title = titleLabelText

    let lblAttributes = [
        NSFontAttributeName: titleFont,
        NSForegroundColorAttributeName: title_font_color,
        ] as [String : Any]

    let origin = CGPoint(
        x: title_label_position.x * rect.w,
        y: title_label_position.y * rect.h)

    let drawRect = CGRect(
        origin: origin,
        size: title.size(attributes: lblAttributes))

    title.drawFlipped(in: drawRect, withAttributes: lblAttributes)
}

Result:

screen shot with correctly drawn title

Note that I only fixed drawTitle(inRect:inContext:), so all of the other text is still upside-down.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download