views:

5358

answers:

6

All I want is a one pixel black border around my white UILabel text.

I got as far as subclassing UILabel with the code below, which I clumsily cobbled together from a few tangentially related online examples. And it works but it's very, very slow (except on the simulator) and I couldn't get it to center the text vertically either (so I hard-coded the y value on the last line temporarily). Ahhhh!

void ShowStringCentered(CGContextRef gc, float x, float y, const char *str) {
    CGContextSetTextDrawingMode(gc, kCGTextInvisible);
    CGContextShowTextAtPoint(gc, 0, 0, str, strlen(str));
    CGPoint pt = CGContextGetTextPosition(gc);

    CGContextSetTextDrawingMode(gc, kCGTextFillStroke);

    CGContextShowTextAtPoint(gc, x - pt.x / 2, y, str, strlen(str));
}


- (void)drawRect:(CGRect)rect{

    CGContextRef theContext = UIGraphicsGetCurrentContext();
    CGRect viewBounds = self.bounds;

    CGContextTranslateCTM(theContext, 0, viewBounds.size.height);
    CGContextScaleCTM(theContext, 1, -1);

    CGContextSelectFont (theContext, "Helvetica", viewBounds.size.height,  kCGEncodingMacRoman);

    CGContextSetRGBFillColor (theContext, 1, 1, 1, 1);
    CGContextSetRGBStrokeColor (theContext, 0, 0, 0, 1);
    CGContextSetLineWidth(theContext, 1.0);

    ShowStringCentered(theContext, rect.size.width / 2.0, 12, [[self text] cStringUsingEncoding:NSASCIIStringEncoding]);
}

I just have a nagging feeling that I'm overlooking a simpler way to do this. Perhaps by overriding "drawTextInRect", but I can't seem to get drawTextInRect to bend to my will at all despite staring at it intently and frowning really really hard.

A: 

Why don't you create a 1px border UIView in Photoshop, then set a UIView with the image, and position it behind your UILabel?

Code:

UIView *myView;
UIImage *imageName = [UIImage imageNamed:@"1pxBorderImage.png"];
UIColor *tempColour = [[UIColor alloc] initWithPatternImage:imageName];
myView.backgroundColor = tempColour;
[tempColour release];

It's going to save you subclassing an object and it's fairly simple to do.

Not to mention if you want to do animation, it's built into the UIView class.

Brock Woolf
The text on the label changes and I need the text itself to have a 1 pixel black outline. (The rest of the UILabel's background is transparent.)
Monte
I thought I understood how this will cause any text I place in the UILabel to be outlined, but I was deliriously tired and now I can't seem to wrap my mind around how it could accomplish that. I'm going to go read up on initWithPatternImage.
Monte
A: 

If you want to animate something complicated, the best way is to programmaticly take a screenshot of it an animate that instead!

To take a screenshot of a view, you'll need code a little like this:

UIGraphicsBeginImageContext(mainContentView.bounds.size);
[mainContentView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Where mainContentView is the view you want to take a screenshot of. Add viewImage to a UIImageView and animate that.

Hope that speeds up your animation!!

N

Nick Cartwright
That's an awesome trick that I can think of a few places to use, and probably solves the performance issue, but i'm still holding out hope that there's a simpler way than my crappy subclass to make uilabel text show an outline. In the mean time I'm gonna play with your example thanks!
Monte
I feel your pain. I'm not sure you'll find a good way for doing that. I often write reams of code to product effects then snapshot them (like above) for smooth animation! For the example above you need to include <QuartzCore/QuartzCore.h> in your code! Best of luck! N
Nick Cartwright
+1  A: 

if ALL you want is a one pixel black border around my white UILabel text,

then i do think you're making the problem harder than it is... I don't know by memory which 'draw rect / frameRect' function you should use, but it will be easy for you to find. this method just demonstrates the strategy (let the superclass do the work!):

- (void)drawRect:(CGRect)rect
{
  [super drawRect:rect];
  [context frameRect:rect]; // research which rect drawing function to use...
}
kent
i don't get it. probably because i've been staring at the screen for about 12 hours and i don't get much at this point...
Monte
you are subclassing UILabel, a class which already draws text. and the reason you are subclassing is because you want to add a black border around the text. so if you let the superclass draw the text, then all you have to do is draw the border, no?
kent
This is a good point... although, if he wants to add a 1 pixel black border, the subclass will probably draw the letters too close together! ....I'd do something like posted in the original question and optimise (as stated in my post)! N
Nick Cartwright
now i get it thanks! will try!
Monte
@nickcartwright: if the case occurs that the superclass is doing as you say, you could inset the CGRect before calling [super drawRect]. still easier and safer than re-writing the functionality of the superclass.
kent
And then @kent left SO in frustration. Great answer, +1.
Yar
+4  A: 

I was able to do it by overriding drawTextInRect:

- (void)drawTextInRect:(CGRect)rect {

  CGSize shadowOffset = self.shadowOffset;
  UIColor *textColor = self.textColor;

  CGContextRef c = UIGraphicsGetCurrentContext();
  CGContextSetLineWidth(c, 1);

  CGContextSetTextDrawingMode(c, kCGTextStroke);
  self.textColor = [UIColor whiteColor];
  [super drawTextInRect:rect];

  CGContextSetTextDrawingMode(c, kCGTextFill);
  self.textColor = textColor;
  self.shadowOffset = CGSizeMake(0, 0);
  [super drawTextInRect:rect];

  self.shadowOffset = shadowOffset;

}
kprevas
i will try that!
Monte
you, sir, are awesome.
Monte
Thank you, thank you, thank you!
Hilton Campbell
A: 

Hi kprevas,

how do I override the drawRect method using your code?

Thanks,

FerrariX
You should ask this as its own question, not as a non-answering answer on another question.
Peter Hosey
A: 

To put a border with rounded edges around a UILabel I do the following:

labelName.layer.borderWidth = 1;
labelName.layer.borderColor = [[UIColor grayColor] CGColor];
labelName.layer.cornerRadius = 10;

(don't forget to include QuartzCore/QuartzCore.h)

mike