Question

I'm adding a fairly wide stroke of a few pixels to text in a UILabel, and depending on the line spacing, if the very edges of the text touch the very edges of the label, the sides of the stroke can be cut off, if they go outside of the bounds of the label. How can I prevent this? Here's the code I'm using to apply the stroke (currently of 5px):

- (void) drawTextInRect: (CGRect) rect
{
    UIColor *textColor = self.textColor;

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(c, 5);
    CGContextSetLineJoin(c, kCGLineJoinRound);
    CGContextSetTextDrawingMode(c, kCGTextStroke);
    self.textColor = [UIColor colorWithRed: 0.165 green: 0.635 blue: 0.843 alpha: 1.0];
    [super drawTextInRect: rect];
}

Here's an example of the clipping at the side of the label, I think what needs to happen is one of the following:

  • That when the text splits onto multiple lines, some space is given inside the label's frame for the stroke to occupy.
  • Or, that the stroke is allowed to overflow the outer bounds of the label.

enter image description here enter image description here

Was it helpful?

Solution

Yup, clipping just didn't work.

What if you create insets on your UILabel sub-class though. You'd make the frame of the label however big you need it to be, then set your insets. When you draw the text, it will use the insets to give you padding around any edge you need.

The downside is, you won't be able to judge line wrapping at a glance in IB. You'd have to take your label and subtract your insets then you'll see what it would really look line on the screen.

.h

@interface MyLabel : UILabel
@property (nonatomic) UIEdgeInsets insets;
@end

.m

@implementation MyLabel

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.insets = UIEdgeInsetsMake(0, 3, 0, 3);
    }

    return self;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSaveGState(c);

    CGRect actualTextContentRect = rect;
    actualTextContentRect.origin.x += self.insets.left;
    actualTextContentRect.origin.y += self.insets.top;
    actualTextContentRect.size.width -= self.insets.right;
    actualTextContentRect.size.height -= self.insets.bottom;

    CGContextSetLineWidth(c, 5);
    CGContextSetLineJoin(c, kCGLineJoinRound);
    CGContextSetTextDrawingMode(c, kCGTextStroke);
    self.textColor = [UIColor colorWithRed: 0.165 green: 0.635 blue: 0.843 alpha: 1.0];

    [super drawTextInRect:actualTextContentRect];
    CGContextRestoreGState(c);
    self.textColor = [UIColor whiteColor];
    [super drawTextInRect:actualTextContentRect];
}

Simulator run

Edit: Added full code for my UILabel subclass. Modified the code slightly to show both the large stroke and the the normal lettering.

OTHER TIPS

You should implement sizeThatFits: on your UILabel subclass to return a slightly larger preferred size, taking the additional space required for the stroke into consideration. You can then either use the result of sizeThatFits: to calculate the label's frame correctly, or just call sizeToFit.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top