ios 如何让 UILabel 显示轮廓文本?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1103148/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I make UILabel display outlined text?
提问by Monte Hurd
All I want is a one pixel black border around my white UILabel text.
我想要的只是我的白色 UILabel 文本周围的一个像素的黑色边框。
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!
我用下面的代码对 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]);
}
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.
我只是有一种唠叨的感觉,我忽略了一种更简单的方法来做到这一点。也许通过覆盖“drawTextInRect”,但我似乎无法让 drawTextInRect 屈服于我的意愿,尽管我专心地盯着它并且真的很难皱眉。
回答by kprevas
I was able to do it by overriding drawTextInRect:
我能够通过覆盖 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;
}
回答by Axel Guilmin
A simpler solution is to use an Attributed Stringlike so:
一个更简单的解决方案是使用属性字符串,如下所示:
Swift 4:
斯威夫特 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:
斯威夫特 4.2:
let strokeTextAttributes: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.black,
.foregroundColor : UIColor.white,
.strokeWidth : -2.0,
]
myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)
On a UITextField
you can set the defaultTextAttributes
and the attributedPlaceholder
as well.
在 a 上,UITextField
您也可以设置defaultTextAttributes
和attributedPlaceholder
。
Note that the NSStrokeWidthAttributeName
has to be negativein this case, i.e. only the inner outlines work.
请注意,在这种情况下NSStrokeWidthAttributeName
必须为负,即只有内部轮廓有效。
回答by Lachezar
After reading the accepted answer and the two corrections to it and the answer from Axel Guilmin, I decided to compile an overall solution in Swift, that suits me:
在阅读了接受的答案及其两个更正以及 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)
}
}
You can add this custom UILabel class to an existing label in the Interface Builder and change the thickness of the border and its color by adding User Defined Runtime Attributes like this:
您可以将此自定义 UILabel 类添加到 Interface Builder 中的现有标签,并通过添加用户定义的运行时属性来更改边框的粗细和颜色,如下所示:
Result:
结果:
回答by tobihagemann
There is one issue with the answer's implementation. Drawing a text with stroke has a slightly different character glyph width than drawing a text without stroke, which can produce "uncentered" results. It can be fixed by adding an invisible stroke around the fill text.
答案的实施存在一个问题。绘制带有笔画的文本的字符字形宽度与绘制不带笔画的文本略有不同,这会产生“不居中”的结果。它可以通过在填充文本周围添加一个不可见的笔划来修复。
Replace:
代替:
CGContextSetTextDrawingMode(c, kCGTextFill);
self.textColor = textColor;
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
with:
和:
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
self.textColor = textColor;
[[UIColor clearColor] setStroke]; // invisible stroke
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
I'm not 100% sure, if that's the real deal, because I don't know if self.textColor = textColor;
has the same effect as [textColor setFill]
, but it should work.
我不是 100% 确定,如果那是真的,因为我不知道是否与self.textColor = textColor;
具有相同的效果[textColor setFill]
,但它应该有效。
Disclosure: I'm the developer of THLabel.
披露:我是 THLabel 的开发者。
I've released a UILabel subclass a while ago, which allows an outline in text and other effects. You can find it here: https://github.com/tobihagemann/THLabel
不久前我发布了一个 UILabel 子类,它允许文本和其他效果的轮廓。你可以在这里找到它:https: //github.com/tobihagemann/THLabel
回答by Suran
This won't create an outline per-se, but it will put a shadow around the text, and if you make the shadow radius small enough it could resemble an outline.
这本身不会创建轮廓,但它会在文本周围放置一个阴影,如果您将阴影半径设置得足够小,它可能类似于一个轮廓。
label.layer.shadowColor = [[UIColor blackColor] CGColor];
label.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
label.layer.shadowOpacity = 1.0f;
label.layer.shadowRadius = 1.0f;
I don't know whether it is compatible with older versions of iOS..
我不知道它是否兼容旧版本的iOS..
Anyway, I hope it helps...
无论如何,我希望它有帮助...
回答by Nick Cartwright
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.
其中 mainContentView 是您要截取屏幕截图的视图。将 viewImage 添加到 UIImageView 并为其设置动画。
Hope that speeds up your animation!!
希望能加快你的动画速度!!
N
N
回答by ufmike
As MuscleRumblementioned, 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.
正如MuscleRumble 所提到的,接受的答案的边界有点偏离中心。我能够通过将笔触宽度设置为零而不是将颜色更改为清除来纠正此问题。
i.e. replacing:
即替换:
CGContextSetTextDrawingMode(c, kCGTextFill);
self.textColor = textColor;
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
with:
和:
CGContextSetTextDrawingMode(c, kCGTextFillStroke);
self.textColor = textColor;
CGContextSetLineWidth(c, 0); // set stroke width to zero
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
I would've just commented on his answer but apparently I'm not "reputable" enough.
我只是对他的回答发表评论,但显然我不够“有信誉”。
回答by Chris Stillwell
A Swift 4 class version based off the answer by 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
}
}
It can be implemented entirely in the Interface Builder by setting the UILabel's custom class to OutlinedText. You will then have the ability to set the outline's width and color from the Properties pane.
通过将 UILabel 的自定义类设置为 OutlinedText,它可以完全在 Interface Builder 中实现。然后,您将能够从“属性”窗格中设置轮廓的宽度和颜色。
回答by kent
if ALL you want is a one pixel black border around my white UILabel text,
如果你想要的只是我的白色 UILabel 文本周围的一个像素的黑色边框,
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!):
那么我确实认为你让问题变得比现在更难......我不知道你应该使用哪个'draw rect / frameRect'函数,但你很容易找到它。这个方法只是演示了策略(让超类来做!):
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
[context frameRect:rect]; // research which rect drawing function to use...
}
回答by Robotbugs
I found an issue with the main answer. The text position is not necessarily centered correctly to sub-pixel location, so that the outline can be mismatched around the text. I fixed it using the following code, which uses CGContextSetShouldSubpixelQuantizeFonts(ctx, false)
:
我发现主要答案有问题。文本位置不一定正确居中到亚像素位置,因此文本周围的轮廓可能不匹配。我使用以下代码修复了它,该代码使用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];
}
This assumes that you defined textOutlineColor
and textOutlineWidth
as properties.
这假设您已将textOutlineColor
和定义textOutlineWidth
为属性。