Dev Blog

UILabels in iOS 6

Posted by lerno on March 22, 2013

I had a lot of people asking me about this, so here comes the solution (finally).

Subclass UILabel, and override drawTextInRect:

- (void)drawTextInRect:(CGRect)rect
{
   // We rely on stroke being set on the attributed text, if it isn't
   // just render this as usual.
   if (![[self attributedText] length])
   {
      [super drawTextInRect:rect];
      return;
   }
 
   // Retrieve the stroke width, assuming there is only a single 
   // stroke width set.
   NSRangePointer pointer = nil;
   CGFloat strokeWidth = [[[self attributedText] 
                              attribute:NSStrokeWidthAttributeName 
                                atIndex:0 
                         effectiveRange:pointer] floatValue];
 
   // Retrieve the stroke colour, assuming there is only one stroke colour set.
   UIColor *strokeColor = [[self attributedText] 
                              attribute:NSStrokeColorAttributeName 
                                atIndex:0 
                         effectiveRange:pointer];
 
   // If width is zero or positive (positive is non-filled outlines!) or no
   // stroke colour is set. Render this as usual.
   if (strokeWidth >= 0 || !strokeColor)
   {
      [super drawTextInRect:rect];
      return;
   }
 
   // Store our original attributed string.
   NSAttributedString *original = [self attributedText];
 
   // Create a copy
   NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] 
      initWithAttributedString:[self attributedText]];
 
   // Select the entire string.
   NSRange allCharacters = NSMakeRange(0, [attributedString length]);
 
   // Set the foreground colour the same as the outline.
   [attributedString addAttribute:NSForegroundColorAttributeName 
                            value:strokeColor 
                            range:allCharacters];
 
   // Draw this text (outline and text in same colour)
   [self setAttributedText:attributedString];
   [super drawTextInRect:rect];
 
   // Create a new string, this with the text colour of the original
   attributedString = [[NSMutableAttributedString alloc] 
      initWithAttributedString:original];
 
   // But set the stroke colour to transparent (this makes the offset 
   // and spacing exactly right)
   [attributedString addAttribute:NSStrokeColorAttributeName 
                            value:[UIColor clearColor] 
                            range:allCharacters];
 
   // Draw this text (normal text colour, transparent outline)
   [self setAttributedText:attributedString];
   [super drawTextInRect:rect];
 
   // Revert the attributed text to the original.
   [self setAttributedText:original];
}

 

This label will behave exactly like a normal UILabel, except if you load it with attributed text containing stroke width and colour attributes. Remember to keep the stroke width negative to trigger outline + fill. Also, you need to actually experiment with the size of the stroke in order to make it look good. Due to the way iOS renders the stroke, you might need to set it to a surprisingly high number. It’s basically a matter of trial and error.

Here’s a snippet to exercise the subclass:

 

NSMutableAttributedString *string = [[NSMutableAttributedString alloc] 
   initWithString:@"Testing"];
[string addAttributes:@{ NSStrokeWidthAttributeName: @(-20), 
                         NSStrokeColorAttributeName: [UIColor whiteColor], 
                         NSForegroundColorAttributeName: [UIColor blackColor] }];
[[self oulinedLabel] setAttributedText:string];

 

Good luck!

Website Design By Vivid in Jacksonville, Florida