如何让UILabel显示描边文字?

120

我想要的只是白色UILabel文本周围一像素的黑色边框。

我已经通过以下代码创建了UILabel子类,这些代码是从一些有关示例中拼凑而成的。它能够工作,但速度非常慢(除了模拟器之外),我也无法使文本在垂直方向上居中(所以我暂时将y值硬编码到了最后一行)。啊啊啊!

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]);
}

我只是有一种困扰的感觉,觉得我可能忽视了一个更简单的方法来做这件事。也许可以通过覆盖“drawTextInRect”来实现,但是尽管我一直盯着它看并且非常用力地皱眉,但似乎无法让“drawTextInRect”顺从于我。


澄清一下 - 我的应用程序变慢是因为我在标签值更改时使用了轻微的增长和缩小动画。如果不使用子类化,它会很流畅,但是使用上面的代码后,标签动画就会非常卡顿。我应该只使用UIWebView吗?我觉得这样做有点傻,因为标签只显示一个数字... - Monte Hurd
好的,看起来我之前遇到的性能问题与大纲代码无关,但我仍然无法垂直对齐。不知何故,pt.y 始终为零。 - Monte Hurd
对于像Chalkduster这样的字体来说,这非常慢。 - jjxtra
15个回答

1
我发现主要答案存在一个问题。文字位置不一定正确居中到亚像素位置,因此可能会导致文字周围的轮廓不匹配。我使用以下代码进行修复,它使用CGContextSetShouldSubpixelQuantizeFonts(ctx, false)
- (void)drawTextInRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    [self.textOutlineColor setStroke];
    [self.textColor setFill];

    CGContextSetShouldSubpixelQuantizeFonts(ctx, false);

    CGContextSetLineWidth(ctx, self.textOutlineWidth);
    CGContextSetLineJoin(ctx, kCGLineJoinRound);

    CGContextSetTextDrawingMode(ctx, kCGTextStroke);
    [self.text drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment];

    CGContextSetTextDrawingMode(ctx, kCGTextFill);
    [self.text drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment];
}

这假设你已将 textOutlineColortextOutlineWidth 定义为属性。

0

这是将轮廓文本设置在标签上的另一个答案。

extension UILabel {

func setOutLinedText(_ text: String) {
    let attribute : [NSAttributedString.Key : Any] = [
        NSAttributedString.Key.strokeColor : UIColor.black,
        NSAttributedString.Key.foregroundColor : UIColor.white,
        NSAttributedString.Key.strokeWidth : -2.0,
        NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 12)
        ] as [NSAttributedString.Key  : Any]

    let customizedText = NSMutableAttributedString(string: text,
                                                   attributes: attribute)
    attributedText = customizedText
 }

}

使用扩展方法轻松设置带轮廓的文本。

lblTitle.setOutLinedText("Enter your email address or username")

这不是大纲,这是内联文本。 - submariner

0

也可以使用以下逻辑对 UILabel 进行子类化:

- (void)setText:(NSString *)text {
    [self addOutlineForAttributedText:[[NSAttributedString alloc] initWithString:text]];
}

- (void)setAttributedText:(NSAttributedString *)attributedText {
    [self addOutlineForAttributedText:attributedText];
}

- (void)addOutlineForAttributedText:(NSAttributedString *)attributedText {
    NSDictionary *strokeTextAttributes = @{
                                           NSStrokeColorAttributeName: [UIColor blackColor],
                                           NSStrokeWidthAttributeName : @(-2)
                                           };

    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText];
    [attrStr addAttributes:strokeTextAttributes range:NSMakeRange(0, attrStr.length)];

    super.attributedText = attrStr;
}

如果您在Storyboard中设置文本,则:

- (instancetype)initWithCoder:(NSCoder *)aDecoder {

    self = [super initWithCoder:aDecoder];
    if (self) {
        // to apply border for text from storyboard
        [self addOutlineForAttributedText:[[NSAttributedString alloc] initWithString:self.text]];
    }
    return self;
}

-3
为什么不在Photoshop中创建一个1像素边框的UIView,然后设置一个带有图像的UIView,并将其放置在UILabel后面?
代码:
UIView *myView;
UIImage *imageName = [UIImage imageNamed:@"1pxBorderImage.png"];
UIColor *tempColour = [[UIColor alloc] initWithPatternImage:imageName];
myView.backgroundColor = tempColour;
[tempColour release];

这将避免您对对象进行子类化,而且非常简单易行。

更不用说如果您想要进行动画,它已经内置在UIView类中了。


标签上的文本会变化,我需要文本本身有1像素的黑色轮廓线。(其余UILabel的背景是透明的。) - Monte Hurd
我原以为我理解了这将如何导致我放置在UILabel中的任何文本都被勾勒出来,但我现在感到非常疲倦,似乎无法理解它是如何实现的。我要去阅读一下initWithPatternImage。 - Monte Hurd

-3

要在UILabel周围放置一个带有圆角的边框,我会执行以下操作:

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

(不要忘记包含QuartzCore/QuartzCore.h)


5
这会在整个标签周围添加一个大边框,而不是只在文本周围添加。 - Pavel Alexeev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接