kCGTextStroke's Fill and Stroke aren't positioned correctly
Asked Answered
N

2

2

So I'm using the code below to apply a stroke (and fill) to text in a UILabel, and it's coming out like the image below. The stroke is heavier on one side than the other (look at the top of the letters compared to the bottom, and the right compared to the left. The period at the end makes it very noticeable, too, looks like a googly eye). I've not got any shadowing turned on at all, so I don't think it's the shadow interfering with the stroke.

What could be causing this?

- (void) drawTextInRect: (CGRect) rect
{
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSetTextDrawingMode(c, kCGTextFillStroke);
    CGContextSaveGState(c);
    CGContextSetRGBFillColor(c, 1.0, 0.0, 0.0, 1.0);
    CGContextSetRGBStrokeColor(c, 0.0, 1.0, 0.0, 1.0);
    [super drawTextInRect: rect];
    CGContextRestoreGState(c);
}

enter image description here

EDIT: So, for kicks, I took at look at the label with only the fill, and only the stroke. Turning off the stroke creates a perfectly normal-looking piece of text, as if I'd just coloured it in Interface Builder. Turning off the fill, however, shows only the stroke, which doesn't look heavier on any side than any other. **This leads me to believe that the issue is where the fill is positioned in relation to the stroke, and that neither the fill or stroke themselves are at fault. Any other thoughts on this? How can I get the fill directly centred in the stroke?

Neurosurgery answered 5/4, 2013 at 16:22 Comment(3)
Shouldn't you either just use kCGTextFillStroke as the drawing mode and only draw once (with separate stroke and fill colors set) or stroke after the fill?Impalpable
@DavidRönnqvist That seems to have helped. If you'd like to write an answer, I can mark it. Seems odd though, that the order would matter, no?Neurosurgery
@Neurosurgery pass 7+ years, someone like me comes across this same issue which still exists (iOS 13.4) when using attributes text of the UILabel. Do you still maintain a code for fill and stroke?Bosomed
B
4

You should probably just use kCGTextFillStroke as the drawing mode and only draw once (with separate stroke and fill colors set).

CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);   // any color you want (this is red)
CGContextSetRGBStrokeColor(context, 0.0, 1.0, 0.0, 1.0); // any color you want (this is green)
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
[self.text drawInRect:rect withFont:self.font];

Alternatively you could just stroke afterwards. Strokes are usually drawn from the center which means that half of the width is inwards and half is outwards. That would mean that if you fill after you stroke some of the stroke is going to get covered up by the fill.

Belief answered 8/4, 2013 at 10:40 Comment(3)
It seems to be ignoring the colour choices, it's just all displaying white (which is weird, since it's black in Interface Builder). I've updated the code above.Neurosurgery
(Both fill and stroke are white).Neurosurgery
The super implementation could possibly override the context colors. You could draw the string yourself using normal drawInRect:withFont: (see my updated answer).Impalpable
R
1

A possibility is that the overridden method translates the CTM using CGContextTranslateCTM or similar functions. The CTM is part of the state of a context and specifies a transform for all following draw calls.

You should try to save the context before the call to the overridden method and restore it afterwards:

CGContextSaveGState(c);
[super drawTextInRect: rect];
CGContextRestoreGState(c);
Rattle answered 8/4, 2013 at 8:42 Comment(7)
That's made no difference at all, guess that's not the issue. Nice try, though :)Neurosurgery
@lukech Do you have access to the overridden method? If so, post that code.Rattle
I've posted the drawTextInRect: method, and I haven't overridden any others – what else do you need to see?Neurosurgery
@lukech In your implementation you are sending [super drawTextInRect: rect], calling the overridden implementation. If the superclass a custom class of yours, please post the code. If you are subclassing UILabel or UITextField obviously you don't have the code. Since these framework classes give not explicit guarantee of how they draw their contents it's probably a bad idea to modify their drawing. You should revise the whole approach.Rattle
It's just a subclass of UILabel. The pre-filled comments in that method say they can be overridden if I'm performing custom drawing – which I am. What other alternative approaches do I have?Neurosurgery
@lukech In this case it might be better not to use the original UILabel's drawing but do all drawing yourself. Just don't call to super and instead use kCGTextFillStroke to stroke and fill the text.Rattle
If I remove the call to super there, no text is drawn at all.Neurosurgery

© 2022 - 2024 — McMap. All rights reserved.