如何使UILabel显示轮廓文字?

Mon*_*urd 102 iphone cocoa-touch core-graphics objective-c ios

我想要的只是在我的白色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]);
}
Run Code Online (Sandbox Code Playgroud)

我只是有一种唠叨的感觉,我忽略了一种更简单的方法.也许通过重写"drawTextInRect",但我似乎无法让drawTextInRect屈服于我的意志,尽管专心地盯着它并且皱眉真的很难.

kpr*_*vas 164

我能够通过覆盖drawTextInRect来做到这一点:

- (void)drawTextInRect:(CGRect)rect {

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

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

  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;

}
Run Code Online (Sandbox Code Playgroud)

  • 我试过这种技术,但结果并不令人满意.在我的iPhone 4上,我尝试使用此代码绘制带有黑色轮廓的白色文本.我得到的是文本中每个字母的左右边缘的黑色轮廓,但不是顶部或底部的轮廓.有任何想法吗? (3认同)

Axe*_*min 104

更简单的解决方案是使用属性字符串,如下所示:

斯威夫特4:

let strokeTextAttributes: [NSAttributedStringKey : Any] = [
    NSAttributedStringKey.strokeColor : UIColor.black,
    NSAttributedStringKey.foregroundColor : UIColor.white,
    NSAttributedStringKey.strokeWidth : -2.0,
    ]

myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)
Run Code Online (Sandbox Code Playgroud)

Swift 4.2:

let strokeTextAttributes: [NSAttributedString.Key : Any] = [
    .strokeColor : UIColor.black,
    .foregroundColor : UIColor.white,
    .strokeWidth : -2.0,
    ]

myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)
Run Code Online (Sandbox Code Playgroud)

在一个UITextField可以设置defaultTextAttributesattributedPlaceholder为好.

请注意,在这种情况下NSStrokeWidthAttributeName 必须是负数,即只有内部轮廓有效.

UITextFields使用属性文本的大纲

  • 为了方便Objective-C,这将是:label.attributedText = [[NSAttributedString alloc] initWithString:@"String"属性:@ {NSStrokeColorAttributeName:[UIColor blackColor],NSForegroundColorAttributeName:[UIColor whiteColor],NSStrokeWidthAttributeName:@ -1.0}] ; (16认同)
  • 使用这种模因有效地传达了这种方法的细微差别 (8认同)

Lac*_*zar 24

在阅读了接受的答案以及对它的两个更正和Axel Guilmin的答案之后,我决定在Swift中编译一个适合我的整体解决方案:

import UIKit

class UIOutlinedLabel: UILabel {

    var outlineWidth: CGFloat = 1
    var outlineColor: UIColor = UIColor.whiteColor()

    override func drawTextInRect(rect: CGRect) {

        let strokeTextAttributes = [
            NSStrokeColorAttributeName : outlineColor,
            NSStrokeWidthAttributeName : -1 * outlineWidth,
        ]

        self.attributedText = NSAttributedString(string: self.text ?? "", attributes: strokeTextAttributes)
        super.drawTextInRect(rect)
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以将此自定义UILabel类添加到Interface Builder中的现有标签,并通过添加用户定义的运行时属性来更改边框的厚度及其颜色,如下所示: Interface Builder设置

结果:

大红色G与大纲


tob*_*ann 8

答案的实现存在一个问题.使用笔划绘制文本的字符字形宽度与绘制没有笔划的文本略有不同,这会产生"未中心"的结果.可以通过在填充文本周围添加不​​可见的笔划来修复它.

更换:

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

有:

CGContextSetTextDrawingMode(context, kCGTextFillStroke);
self.textColor = textColor;
[[UIColor clearColor] setStroke]; // invisible stroke
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
Run Code Online (Sandbox Code Playgroud)

我不是百分百肯定,如果这是真正的交易,因为我不知道是否self.textColor = textColor;有相同的效果[textColor setFill],但它应该工作.

披露:我是THLabel的开发者.

我刚刚发布了一个UILabel子类,它允许在文本和其他效果中使用大纲.你可以在这里找到它:https://github.com/tobihagemann/THLabel

  • 刚刚使用过THLabel,生活很复杂 (4认同)

Sur*_*ran 6

这不会创建一个轮廓本身,但它会在文本周围留下阴影,如果你使阴影半径足够小,它可能类似于轮廓.

label.layer.shadowColor = [[UIColor blackColor] CGColor];
label.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
label.layer.shadowOpacity = 1.0f;
label.layer.shadowRadius = 1.0f;
Run Code Online (Sandbox Code Playgroud)

我不知道它是否与旧版本的iOS兼容..

无论如何,我希望它有所帮助......

  • 这不是标签周围的一个像素边框.这是一个简单的阴影到底部. (9认同)

Chr*_*ell 5

基于kprevas答案的Swift 4 类版本

import Foundation
import UIKit

public class OutlinedText: UILabel{
    internal var mOutlineColor:UIColor?
    internal var mOutlineWidth:CGFloat?

    @IBInspectable var outlineColor: UIColor{
        get { return mOutlineColor ?? UIColor.clear }
        set { mOutlineColor = newValue }
    }

    @IBInspectable var outlineWidth: CGFloat{
        get { return mOutlineWidth ?? 0 }
        set { mOutlineWidth = newValue }
    }    

    override public func drawText(in rect: CGRect) {
        let shadowOffset = self.shadowOffset
        let textColor = self.textColor

        let c = UIGraphicsGetCurrentContext()
        c?.setLineWidth(outlineWidth)
        c?.setLineJoin(.round)
        c?.setTextDrawingMode(.stroke)
        self.textColor = mOutlineColor;
        super.drawText(in:rect)

        c?.setTextDrawingMode(.fill)
        self.textColor = textColor
        self.shadowOffset = CGSize(width: 0, height: 0)
        super.drawText(in:rect)

        self.shadowOffset = shadowOffset
    }
}
Run Code Online (Sandbox Code Playgroud)

通过将 UILabel 的自定义类设置为 OutlinedText,它可以完全在 Interface Builder 中实现。然后,您将能够从“属性”窗格中设置轮廓的宽度和颜色。

在此处输入图片说明


Gas*_* J. 5

如果你的目标是这样的:

在此输入图像描述

以下是我实现它的方法:我添加了一个新的label自定义类作为子视图到我当前的 UILabel (受到此答案的启发)。

只需将其复制并粘贴到您的项目中即可:

extension UILabel {
    func addTextOutline(usingColor outlineColor: UIColor, outlineWidth: CGFloat) {
        class OutlinedText: UILabel{
            var outlineWidth: CGFloat = 0
            var outlineColor: UIColor = .clear

            override public func drawText(in rect: CGRect) {
                let shadowOffset = self.shadowOffset
                let textColor = self.textColor

                let c = UIGraphicsGetCurrentContext()
                c?.setLineWidth(outlineWidth)
                c?.setLineJoin(.round)
                c?.setTextDrawingMode(.stroke)
                self.textAlignment = .center
                self.textColor = outlineColor
                super.drawText(in:rect)

                c?.setTextDrawingMode(.fill)
                self.textColor = textColor
                self.shadowOffset = CGSize(width: 0, height: 0)
                super.drawText(in:rect)

                self.shadowOffset = shadowOffset
            }
        }

        let textOutline = OutlinedText()
        let outlineTag = 9999

        if let prevTextOutline = viewWithTag(outlineTag) {
            prevTextOutline.removeFromSuperview()
        }

        textOutline.outlineColor = outlineColor
        textOutline.outlineWidth = outlineWidth
        textOutline.textColor = textColor
        textOutline.font = font
        textOutline.text = text
        textOutline.tag = outlineTag

        sizeToFit()
        addSubview(textOutline)
        textOutline.frame = CGRect(x: -(outlineWidth / 2), y: -(outlineWidth / 2),
                                   width: bounds.width + outlineWidth,
                                   height: bounds.height + outlineWidth)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

yourLabel.addTextOutline(usingColor: .red, outlineWidth: 6)
Run Code Online (Sandbox Code Playgroud)

它也适用于 aUIButton及其所有动画:

yourButton.titleLabel?.addTextOutline(usingColor: .red, outlineWidth: 6)
Run Code Online (Sandbox Code Playgroud)

  • 这个答案比其他一些答案更好,因为它不是在角色内部绘制,而是在角色周围绘制。对于其他一些答案,如果将宽度增加到足够大,则不再有字符 - 只是轮廓。 (2认同)