Как сделать отображаемый текст в UILabel?
Все, что я хочу, - это одна пиксельная черная рамка вокруг моего белого текста 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 согнуть мою волю, несмотря на то, что внимательно смотрел на нее и нахмурился действительно очень тяжело.
Ответы
Ответ 1
Я смог сделать это, переопределив 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;
}
Ответ 2
Более простое решение - использовать Attributed String следующим образом:
Свифт 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
вы можете установить attributedPlaceholder
defaultTextAttributes
а также attributedPlaceholder
.
Обратите внимание, что NSStrokeWidthAttributeName
должен быть отрицательным в этом случае, т.е. работают только внутренние контуры.
![UITextFields with an outline using attributed texts]()
Ответ 3
Прочитав принятый ответ и две поправки к нему и ответ от Акселя Гильмина, я решил составить общее решение в 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 и изменить толщину границы и ее цвет, добавив атрибуты User Rinedime, такие как:
![Interface Builder setup]()
Результат:
![big red G with outline]()
Ответ 4
Существует одна проблема с реализацией ответа. Рисование текста с инсультом имеет немного отличающуюся ширину символа символа, чем рисование текста без штриха, что может привести к "нецентральным" результатам. Его можно исправить добавлением невидимого штриха вокруг текста заливки.
Заменить:
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
Ответ 5
Это не приведет к созданию схемы в отдельности, но она поместит тень вокруг текста, и если вы сделаете радиус тени достаточно маленьким, это может напоминать контур.
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..
В любом случае, я надеюсь, что это поможет...
Ответ 6
Если вы хотите анимировать что-то сложное, лучший способ - программно сделать снимок экрана из него вместо анимации!
Чтобы сделать снимок экрана, вам понадобится код примерно так:
UIGraphicsBeginImageContext(mainContentView.bounds.size);
[mainContentView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Где mainContentView - это представление, на которое вы хотите сделать снимок экрана. Добавьте viewImage в UIImageView и анимируйте его.
Надеюсь, что ускорит вашу анимацию!
N
Ответ 7
Как упоминалось MuscleRumble, принятая граница ответа немного не в центре. Я смог исправить это, установив ширину штриха в ноль вместо изменения цвета для очистки.
то есть. замена:
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];
Я бы просто прокомментировал его ответ, но, видимо, я недостаточно "авторитетен".
Ответ 8
если ВСЕ, что вы хотите, - это одна пиксельная черная рамка вокруг моего белого текста UILabel,
то
я действительно думаю, что вы делаете проблему сложнее, чем она...
Я не знаю по памяти, какую функцию draw/frameRect вы используете, но вам будет легко найти. этот метод просто демонстрирует стратегию (пусть суперкласс выполнит работу!):
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
[context frameRect:rect]; // research which rect drawing function to use...
}
Ответ 9
Версия класса Swift 4, основанная на ответе 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. После этого вы сможете установить ширину и цвет контура на панели свойств.
![enter image description here]()
Ответ 10
Я нашел проблему с основным ответом. Текстовое положение не обязательно правильно центрируется в субпиксельном местоположении, так что контур может быть несовместим с текстом. Я исправил его, используя следующий код, который использует 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];
}
Предполагается, что вы определили textOutlineColor
и textOutlineWidth
как свойства.
Ответ 11
Вот еще один ответ, чтобы установить выделенный текст на этикетке.
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")
Ответ 12
также возможно создать подкласс 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;
}
Ответ 13
Почему бы вам не создать UIView границы 1px в Photoshop, а затем установить UIView с изображением и разместить его позади своего UILabel?
код:
UIView *myView;
UIImage *imageName = [UIImage imageNamed:@"1pxBorderImage.png"];
UIColor *tempColour = [[UIColor alloc] initWithPatternImage:imageName];
myView.backgroundColor = tempColour;
[tempColour release];
Это поможет вам подклассифицировать объект, и это довольно просто сделать.
Не говоря уже о том, хотите ли вы сделать анимацию, она встроена в класс UIView.
Ответ 14
Чтобы поместить границу с закругленными краями вокруг UILabel, я делаю следующее:
labelName.layer.borderWidth = 1;
labelName.layer.borderColor = [[UIColor grayColor] CGColor];
labelName.layer.cornerRadius = 10;
(не забудьте включить QuartzCore/QuartzCore.h)