Нет встроенного подкласса UIView
, который делает это (кроме UIWebView
, если вы пишете правильный HTML и CSS), но это довольно легко сделать с помощью Core Text. Я поместил свой тестовый проект в мой репозиторий ShapedLabel github , и вот как это выглядит:
![ShapedLabel screen shot](https://i.stack.imgur.com/mXSng.png)
Проект имеет подкласс UIView
с именем ShapedLabel
. Вот как это работает.
Создать подкласс UIView
с именем ShapedLabel
. Дайте ему эти свойства:
@property (nonatomic, copy) NSString *text;
@property (nonatomic) UITextAlignment textAlignment;
@property (nonatomic, copy) NSString *fontName;
@property (nonatomic) CGFloat fontSize;
@property (nonatomic, strong) UIColor *textColor;
@property (nonatomic, strong) UIColor *shapeColor;
@property (nonatomic, copy) UIBezierPath *path;
Вы хотите переопределить каждый метод установки свойств для отправки setNeedsDisplay
, например, так:
- (void)setFontName:(NSString *)fontName {
_fontName = [fontName copy];
[self setNeedsDisplay];
}
Я полагаюсь на ARC, чтобы беспокоиться о выпуске старого значения _fontName
. Если вы не используете ARC ... начните. Это намного проще и поддерживается начиная с iOS 4.0.
В любом случае, вам нужно будет внедрить drawRect:
, где будет выполнена настоящая работа. Сначала мы заполним форму с помощью shapeColor
, если она установлена:
- (void)drawRect:(CGRect)rect
{
if (!_path)
return;
if (_shapeColor) {
[_shapeColor setFill];
[_path fill];
}
Мы проверяем, чтобы у нас были все остальные параметры, которые нам нужны:
if (!_text || !_textColor || !_fontName || _fontSize <= 0)
return;
Далее мы обрабатываем свойство textAligment
:
CTTextAlignment textAlignment = NO ? 0
: _textAlignment == UITextAlignmentCenter ? kCTCenterTextAlignment
: _textAlignment == UITextAlignmentRight ? kCTRightTextAlignment
: kCTLeftTextAlignment;
CTParagraphStyleSetting paragraphStyleSettings[] = {
{
.spec = kCTParagraphStyleSpecifierAlignment,
.valueSize = sizeof textAlignment,
.value = &textAlignment
}
};
CTParagraphStyleRef style = CTParagraphStyleCreate(paragraphStyleSettings, sizeof paragraphStyleSettings / sizeof *paragraphStyleSettings);
Мы создаем CTFont
рядом. Обратите внимание, что это отличается от CGFont
или UIFont
. Вы можете конвертировать CGFont
в CTFont
, используя CTFontCreateWithGraphicsFont
, но вы не можете легко конвертировать UIFont
в CTFont
. В любом случае мы просто создаем CTFont
напрямую:
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)_fontName, _fontSize, NULL);
Мы создаем словарь атрибутов, который определяет все атрибуты стиля, которые мы хотим видеть:
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)font, kCTFontAttributeName,
_textColor.CGColor, kCTForegroundColorAttributeName,
style, kCTParagraphStyleAttributeName,
nil];
CFRelease(font);
CFRelease(style);
Получив словарь атрибутов, мы можем создать атрибутивную строку, которая присоединяет словарь атрибутов к текстовой строке. Вот что использует Core Text:
CFAttributedStringRef trib = CFAttributedStringCreate(NULL, (__bridge CFStringRef)_text, (__bridge CFDictionaryRef)attributes);
Мы создадим фрейм-установщик Core Text, который будет размещать текст из приписанной строки:
CTFramesetterRef setter = CTFramesetterCreateWithAttributedString(trib);
CFRelease(trib);
Core Text предполагает, что графический контекст будет иметь «стандартную» систему координат Core Graphics с началом координат в левом нижнем углу. Но UIKit меняет контекст, чтобы поместить источник в верхнем левом углу. Предположим, что путь был создан с учетом этого. Итак, нам нужно преобразование, которое переворачивает систему координат по вертикали:
// Core Text lays out text using the default Core Graphics coordinate system, with the origin at the lower left. We need to compensate for that, both when laying out the text and when drawing it.
CGAffineTransform textMatrix = CGAffineTransformIdentity;
textMatrix = CGAffineTransformTranslate(textMatrix, 0, self.bounds.size.height);
textMatrix = CGAffineTransformScale(textMatrix, 1, -1);
Затем мы можем создать перевернутую копию пути:
CGPathRef flippedPath = CGPathCreateCopyByTransformingPath(_path.CGPath, &textMatrix);
Наконец, мы можем попросить фреймсеттера выложить фрейм текста. Вот что в действительности соответствует тексту внутри формы, определенной свойством path
:
CTFrameRef frame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), flippedPath, NULL);
CFRelease(flippedPath);
CFRelease(setter);
Наконец, мы рисуем текст. Нам нужно снова
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
CGContextConcatCTM(gc, textMatrix);
CTFrameDraw(frame, gc);
} CGContextRestoreGState(gc);
CFRelease(frame);
}
Вот и все. Теперь вы можете разместить на экране ярлык красивой формы.
Для потомков (на случай, если я удалю тестовый проект), вот полный исходный код класса ShapedLabel
.
ShapedLabel.h
#import <UIKit/UIKit.h>
@interface ShapedLabel : UIView
@property (nonatomic, copy) NSString *text;
@property (nonatomic) UITextAlignment textAlignment;
@property (nonatomic, copy) NSString *fontName;
@property (nonatomic) CGFloat fontSize;
@property (nonatomic, strong) UIColor *textColor;
@property (nonatomic, strong) UIColor *shapeColor;
@property (nonatomic, copy) UIBezierPath *path;
@end
ShapedLabel.m
#import "ShapedLabel.h"
#import <CoreText/CoreText.h>
@implementation ShapedLabel
@synthesize fontName = _fontName;
@synthesize fontSize = _fontSize;
@synthesize path = _path;
@synthesize text = _text;
@synthesize textColor = _textColor;
@synthesize shapeColor = _shapeColor;
@synthesize textAlignment = _textAlignment;
- (void)commonInit {
_text = @"";
_fontSize = UIFont.systemFontSize;
// There is no API for just getting the system font name, grr...
UIFont *uiFont = [UIFont systemFontOfSize:_fontSize];
_fontName = [uiFont.fontName copy];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)setFontName:(NSString *)fontName {
_fontName = [fontName copy];
[self setNeedsDisplay];
}
- (void)setFontSize:(CGFloat)fontSize {
_fontSize = fontSize;
[self setNeedsDisplay];
}
- (void)setPath:(UIBezierPath *)path {
_path = [path copy];
[self setNeedsDisplay];
}
- (void)setText:(NSString *)text {
_text = [text copy];
[self setNeedsDisplay];
}
- (void)setTextColor:(UIColor *)textColor {
_textColor = textColor;
[self setNeedsDisplay];
}
- (void)setTextAlignment:(UITextAlignment)textAlignment {
_textAlignment = textAlignment;
[self setNeedsDisplay];
}
- (void)setShapeColor:(UIColor *)shapeColor {
_shapeColor = shapeColor;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
if (!_path)
return;
if (_shapeColor) {
[_shapeColor setFill];
[_path fill];
}
if (!_text || !_textColor || !_fontName || _fontSize <= 0)
return;
CTTextAlignment textAlignment = NO ? 0
: _textAlignment == UITextAlignmentCenter ? kCTCenterTextAlignment
: _textAlignment == UITextAlignmentRight ? kCTRightTextAlignment
: kCTLeftTextAlignment;
CTParagraphStyleSetting paragraphStyleSettings[] = {
{
.spec = kCTParagraphStyleSpecifierAlignment,
.valueSize = sizeof textAlignment,
.value = &textAlignment
}
};
CTParagraphStyleRef style = CTParagraphStyleCreate(paragraphStyleSettings, sizeof paragraphStyleSettings / sizeof *paragraphStyleSettings);
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)_fontName, _fontSize, NULL);
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)font, kCTFontAttributeName,
_textColor.CGColor, kCTForegroundColorAttributeName,
style, kCTParagraphStyleAttributeName,
nil];
CFRelease(font);
CFRelease(style);
CFAttributedStringRef trib = CFAttributedStringCreate(NULL, (__bridge CFStringRef)_text, (__bridge CFDictionaryRef)attributes);
CTFramesetterRef setter = CTFramesetterCreateWithAttributedString(trib);
CFRelease(trib);
// Core Text lays out text using the default Core Graphics coordinate system, with the origin at the lower left. We need to compensate for that, both when laying out the text and when drawing it.
CGAffineTransform textMatrix = CGAffineTransformIdentity;
textMatrix = CGAffineTransformTranslate(textMatrix, 0, self.bounds.size.height);
textMatrix = CGAffineTransformScale(textMatrix, 1, -1);
CGPathRef flippedPath = CGPathCreateCopyByTransformingPath(_path.CGPath, &textMatrix);
CTFrameRef frame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), flippedPath, NULL);
CFRelease(flippedPath);
CFRelease(setter);
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
CGContextConcatCTM(gc, textMatrix);
CTFrameDraw(frame, gc);
} CGContextRestoreGState(gc);
CFRelease(frame);
}
@end