How do I make UILabel display outlined text?

我所需要的是一个像素的黑色边框围绕我的白色 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]);
}

我只是觉得自己忽略了一个更简单的方法。也许是通过覆盖“ dratTextInRect”,但我似乎无法让 dratTextInRect 屈服于我的意志,尽管我专注地盯着它,皱起眉头非常非常努力。

72121 次浏览

为什么不在 Photoshop 中创建一个1px 的边框 UIView,然后设置图像的 UIView,并将它放置在 UILabel 的后面?

Code:

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

这样可以保存对象的子类化,而且操作相当简单。

更不用说,如果你想做动画,它是内置在 UIView 类。

如果你想动画复杂的东西,最好的方法是编程采取它的一个动画截图,而不是!

要截取视图的屏幕快照,您需要的代码有点像下面这样:

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

其中 mainContentView 是要截屏的视图。将 viewImage 添加到 UIImageView 并将其动画化。

希望这能加快你的动画! !

N

如果所有您想要的是一个像素黑色边框围绕我的白色 UILabel 文本,

那么 我觉得你把问题搞得更复杂了。 我不知道你应该使用什么样的“画 rect/frameRect”函数,但是很容易找到。这个方法只是演示了策略(让超类来完成这项工作!):

- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
[context frameRect:rect]; // research which rect drawing function to use...
}

我可以通过重写 dratTextInRect:

- (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;


}

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;

(别忘了加上 QuartzCore/QuartzCore.h)

这不会创建一个轮廓本身,但它会把一个阴影周围的文字,如果你使阴影半径足够小,它可以类似于一个轮廓。

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

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

不管怎样,我希望这能帮上忙。

我发现主要答案有问题。文本位置不一定位于子像素位置的正确中心,因此轮廓可能与文本周围不匹配。我用下面的代码修复了它,它使用了 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定义为属性。

答案的实现存在一个问题。使用笔画绘制文本与使用不使用笔画绘制文本相比,字符符号宽度略有不同,这会产生“不居中”的结果。可以通过在填充文本周围添加一个不可见的笔划来修复它。

更换:

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

与:

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

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

披露: 我是 THLabel 的开发者。

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

As 肌肉隆隆 mentioned, the accepted answer's border is a bit off center. I was able to correct this by setting the stroke width to zero instead of changing the color to clear.

换言之:

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

与:

CGContextSetTextDrawingMode(c, kCGTextFillStroke);
self.textColor = textColor;
CGContextSetLineWidth(c, 0); // set stroke width to zero
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];

我本可以对他的回答发表评论,但显然我不够“有声望”。

A simpler solution is to use an 属性字符串 like so:

迅捷4:

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


myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)

Swift 4.2:

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


myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)

在一个 UITextField你可以设置的 defaultTextAttributesattributedPlaceholder以及。

请注意,在这种情况下,NSStrokeWidthAttributeName 肯定是阴性,即只有内部轮廓工作。

UITextFields with an outline using attributed texts

在阅读了接受的答案和两个修正后,我决定用 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)
}
}

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

结果:

big red G with outline

A Swift 4 class version based off the 回答者: kprevas

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
}
}

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

enter image description here

下面是在标签上设置大纲文本的另一个答案。

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")

也可以用以下逻辑子类化 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;
}

如果你在故事板中设置了文本,那么:

- (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;
}

如果你的目标是这样的:

enter image description here

下面是我实现它的方法: 我将一个定制类的新 label作为 Subview 添加到我当前的 UILabel 中(受这个 answer的启发)。

只要把它复制粘贴到你的项目中,你就可以开始了:

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

用法:

yourLabel.addTextOutline(usingColor: .red, outlineWidth: 6)

它也适用于所有动画的 UIButton:

yourButton.titleLabel?.addTextOutline(usingColor: .red, outlineWidth: 6)