本文介紹了使用蘋果提供的CoreText框架繪制出弧形效果文字的方法
CoreText是由蘋果官方提供的文本引擎,它提供了多種控制文字布局的方式潜支,通過使用CoreText框架可以控制文字的位置、顏色暖途、尺寸等
蘋果官方提供了一段可以繪制弧形文字的代碼芜飘,使用起來非常方便泪蔫,地址是:
https://developer.apple.com/library/mac/samplecode/CoreTextArcCocoa/Introduction/Intro.html
注意:本文中我們將修改CoreTextArcView.m中的部分代碼堪簿,使得調用接口所得到的圖形更容易理解和辨識(源代碼調試起來比較花時間慨菱,這里將不做對比,只陳述修改后的調試策略戴甩,本篇代碼改動純屬個人喜好)
首先我們在工程中創(chuàng)建新的文件,這里我們就命名為CoreTextArcView闪彼,并將以下代碼導入到.h和.m文件中:
// CoreTextArcView.h
#import <UIKit/UIKit.h>
#import <CoreText/CoreText.h>
@interface CoreTextArcView : UIView {
@private
UIFont * _font;
NSString * _string;
CGFloat _radius;
UIColor * _color;
CGFloat _arcSize;
CGFloat _shiftH, _shiftV; // horiz & vertical shift
struct {
unsigned int showsGlyphBounds:1;
unsigned int showsLineMetrics:1;
unsigned int dimsSubstitutedGlyphs:1;
unsigned int reserved:29;
} _flags;
}
@property(retain, nonatomic) UIFont *font;
@property(retain, nonatomic) NSString *text;
@property(readonly, nonatomic) NSAttributedString *attributedString;
@property(assign, nonatomic) CGFloat radius;
@property(nonatomic) BOOL showsGlyphBounds;
@property(nonatomic) BOOL showsLineMetrics;
@property(nonatomic) BOOL dimsSubstitutedGlyphs;
@property(retain, nonatomic) UIColor *color;
@property(nonatomic) CGFloat arcSize;
@property(nonatomic) CGFloat shiftH, shiftV;
- (instancetype)initWithFrame:(CGRect)frame font:(UIFont *)font text:(NSString *)text radius:(float)radius arcSize:(float)arcSize color:(UIColor *)color;
@end
// CoreTextArcView.m
#import "CoreTextArcView.h"
#import <AssertMacros.h>
#import <QuartzCore/QuartzCore.h>
#define ARCVIEW_DEBUG_MODE NO
#define ARCVIEW_DEFAULT_FONT_NAME @"Helvetica"
#define ARCVIEW_DEFAULT_FONT_SIZE 24.0
#define ARCVIEW_DEFAULT_RADIUS -100.0
#define ARCVIEW_DEFAULT_ARC_SIZE -80.0
@implementation CoreTextArcView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.font = [UIFont fontWithName:ARCVIEW_DEFAULT_FONT_NAME size:ARCVIEW_DEFAULT_FONT_SIZE];
self.text = @"Curva Style Label";
self.radius = ARCVIEW_DEFAULT_RADIUS;
self.showsGlyphBounds = NO;
self.showsLineMetrics = NO;
self.dimsSubstitutedGlyphs = NO;
self.color = [UIColor grayColor];
self.arcSize = ARCVIEW_DEFAULT_ARC_SIZE;
self.shiftH = self.shiftV = 0.0f;
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame font:(UIFont *)font text:(NSString *)text radius:(float)radius arcSize:(float)arcSize color:(UIColor *)color{
self = [super initWithFrame:frame];
if (self) {
self.font = font;
self.text = text;
self.radius = radius;
self.showsGlyphBounds = NO;
self.showsLineMetrics = NO;
self.dimsSubstitutedGlyphs = NO;
self.color = color;
self.arcSize = arcSize;
self.shiftH = self.shiftV = 0.0f;
}
return self;
}
typedef struct GlyphArcInfo {
CGFloat width;
CGFloat angle; // in radians
} GlyphArcInfo;
static void PrepareGlyphArcInfo(CTLineRef line, CFIndex glyphCount, GlyphArcInfo *glyphArcInfo, CGFloat arcSizeRad)
{
NSArray *runArray = (NSArray *)CTLineGetGlyphRuns(line);
// Examine each run in the line, updating glyphOffset to track how far along the run is in terms of glyphCount.
CFIndex glyphOffset = 0;
for (id run in runArray) {
CFIndex runGlyphCount = CTRunGetGlyphCount((CTRunRef)run);
// Ask for the width of each glyph in turn.
CFIndex runGlyphIndex = 0;
for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) {
glyphArcInfo[runGlyphIndex + glyphOffset].width = CTRunGetTypographicBounds((CTRunRef)run, CFRangeMake(runGlyphIndex, 1), NULL, NULL, NULL);
}
glyphOffset += runGlyphCount;
}
double lineLength = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CGFloat prevHalfWidth = glyphArcInfo[0].width / 2.0;
glyphArcInfo[0].angle = (prevHalfWidth / lineLength) * arcSizeRad;
// Divide the arc into slices such that each one covers the distance from one glyph's center to the next.
CFIndex lineGlyphIndex = 1;
for (; lineGlyphIndex < glyphCount; lineGlyphIndex++) {
CGFloat halfWidth = glyphArcInfo[lineGlyphIndex].width / 2.0;
CGFloat prevCenterToCenter = prevHalfWidth + halfWidth;
glyphArcInfo[lineGlyphIndex].angle = (prevCenterToCenter / lineLength) * arcSizeRad;
prevHalfWidth = halfWidth;
}
}
// ensure that redraw occurs.
-(void)setText:(NSString *)text{
_string = [NSString stringWithFormat:@"%@",text];
[self setNeedsDisplay];
}
//set arc size in degrees (180 = half circle)
-(void)setArcSize:(CGFloat)degrees{
_arcSize = degrees * M_PI/180.0;
}
//get arc size in degrees
-(CGFloat)arcSize{
return _arcSize * 180.0/M_PI;
}
- (void)drawRect:(CGRect)rect {
// Don't draw if we don't have a font or string
if (self.font == NULL || self.text == NULL)
return;
// Initialize the text matrix to a known value
CGContextRef context = UIGraphicsGetCurrentContext();
//Reset the transformation
//Doing this means you have to reset the contentScaleFactor to 1.0
CGAffineTransform t0 = CGContextGetCTM(context);
CGFloat xScaleFactor = t0.a > 0 ? t0.a : -t0.a;
CGFloat yScaleFactor = t0.d > 0 ? t0.d : -t0.d;
t0 = CGAffineTransformInvert(t0);
if (xScaleFactor != 1.0 || yScaleFactor != 1.0)
t0 = CGAffineTransformScale(t0, xScaleFactor, yScaleFactor);
CGContextConcatCTM(context, t0);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
if(ARCVIEW_DEBUG_MODE){
// Draw a black background (debug)
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, self.layer.bounds);
}
NSAttributedString *attStr = self.attributedString;
CFAttributedStringRef asr = (__bridge CFAttributedStringRef)attStr;
CTLineRef line = CTLineCreateWithAttributedString(asr);
assert(line != NULL);
CFIndex glyphCount = CTLineGetGlyphCount(line);
if (glyphCount == 0) {
CFRelease(line);
return;
}
GlyphArcInfo * glyphArcInfo = (GlyphArcInfo*)calloc(glyphCount, sizeof(GlyphArcInfo));
PrepareGlyphArcInfo(line, glyphCount, glyphArcInfo, _arcSize);
// Move the origin from the lower left of the view nearer to its center.
CGContextSaveGState(context);
CGContextTranslateCTM(context, CGRectGetMidX(rect)+_shiftH, CGRectGetMidY(rect)+_shiftV - self.radius / 2.0); //注意這是要修改的一行
if(ARCVIEW_DEBUG_MODE){
// Stroke the arc in red for verification.
CGContextBeginPath(context);
CGContextAddArc(context, 0.0, 0.0, self.radius, M_PI_2+_arcSize/2.0, M_PI_2-_arcSize/2.0, 1);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextStrokePath(context);
}
// Rotate the context 90 degrees counterclockwise (per 180 degrees)
CGContextRotateCTM(context, _arcSize/2.0);
// Now for the actual drawing. The angle offset for each glyph relative to the previous glyph has already been calculated; with that information in hand, draw those glyphs overstruck and centered over one another, making sure to rotate the context after each glyph so the glyphs are spread along a semicircular path.
CGPoint textPosition = CGPointMake(0.0, self.radius);
CGContextSetTextPosition(context, textPosition.x, textPosition.y);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
CFIndex runCount = CFArrayGetCount(runArray);
CFIndex glyphOffset = 0;
CFIndex runIndex = 0;
for (; runIndex < runCount; runIndex++) {
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CFIndex runGlyphCount = CTRunGetGlyphCount(run);
Boolean drawSubstitutedGlyphsManually = false;
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
// Determine if we need to draw substituted glyphs manually. Do so if the runFont is not the same as the overall font.
if (self.dimsSubstitutedGlyphs && ![self.font isEqual:(__bridge UIFont *)runFont]) {
drawSubstitutedGlyphsManually = true;
}
CFIndex runGlyphIndex = 0;
for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) {
CFRange glyphRange = CFRangeMake(runGlyphIndex, 1);
CGContextRotateCTM(context, -(glyphArcInfo[runGlyphIndex + glyphOffset].angle));
// Center this glyph by moving left by half its width.
CGFloat glyphWidth = glyphArcInfo[runGlyphIndex + glyphOffset].width;
CGFloat halfGlyphWidth = glyphWidth / 2.0;
CGPoint positionForThisGlyph = CGPointMake(textPosition.x - halfGlyphWidth, textPosition.y);
// Glyphs are positioned relative to the text position for the line, so offset text position leftwards by this glyph's width in preparation for the next glyph.
textPosition.x -= glyphWidth;
CGAffineTransform textMatrix = CTRunGetTextMatrix(run);
textMatrix.tx = positionForThisGlyph.x;
textMatrix.ty = positionForThisGlyph.y;
CGContextSetTextMatrix(context, textMatrix);
if (!drawSubstitutedGlyphsManually) {
CTRunDraw(run, context, glyphRange);
}
else {
// We need to draw the glyphs manually in this case because we are effectively applying a graphics operation by setting the context fill color. Normally we would use kCTForegroundColorAttributeName, but this does not apply as we don't know the ranges for the colors in advance, and we wanted demonstrate how to manually draw.
CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, glyphRange, &glyph);
CTRunGetPositions(run, glyphRange, &position);
CGContextSetFont(context, cgFont);
CGContextSetFontSize(context, CTFontGetSize(runFont));
CGContextSetRGBFillColor(context, 0.25, 0.25, 0.25, 0.5);
CGContextShowGlyphsAtPositions(context, &glyph, &position, 1);
CFRelease(cgFont);
}
// Draw the glyph bounds
if ((self.showsGlyphBounds) != 0) {
CGRect glyphBounds = CTRunGetImageBounds(run, context, glyphRange);
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextStrokeRect(context, glyphBounds);
}
// Draw the bounding boxes defined by the line metrics
if ((self.showsLineMetrics) != 0) {
CGRect lineMetrics;
CGFloat ascent, descent;
CTRunGetTypographicBounds(run, glyphRange, &ascent, &descent, NULL);
// The glyph is centered around the y-axis
lineMetrics.origin.x = -halfGlyphWidth;
lineMetrics.origin.y = positionForThisGlyph.y - descent;
lineMetrics.size.width = glyphWidth;
lineMetrics.size.height = ascent + descent;
CGContextSetRGBStrokeColor(context, 0.0, 1.0, 0.0, 1.0);
CGContextStrokeRect(context, lineMetrics);
}
}
glyphOffset += runGlyphCount;
}
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextSetAlpha(context,0.0);
CGContextFillRect(context, rect);
CGContextRestoreGState(context);
free(glyphArcInfo);
CFRelease(line);
}
@synthesize font = _font;
@synthesize text = _string;
@synthesize radius = _radius;
@synthesize color = _color;
@synthesize arcSize = _arcSize;
@synthesize shiftH = _shiftH;
@synthesize shiftV = _shiftV;
@dynamic attributedString;
- (NSAttributedString *)attributedString {
// Create an attributed string with the current font and string.
assert(self.font != nil);
assert(self.text != nil);
// Create our attributes...
// font
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)self.font.fontName, self.font.pointSize, NULL);
// color
CGColorRef colorRef = self.color.CGColor;
// pack it into attributes dictionary
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)fontRef, (id)kCTFontAttributeName,
colorRef, (id)kCTForegroundColorAttributeName,
nil];
assert(attributesDict != nil);
// Create the attributed string
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:self.text attributes:attributesDict];
return attrString;
}
@dynamic showsGlyphBounds;
- (BOOL)showsGlyphBounds {
return _flags.showsGlyphBounds;
}
- (void)setShowsGlyphBounds:(BOOL)show {
_flags.showsGlyphBounds = show ? 1 : 0;
}
@dynamic showsLineMetrics;
- (BOOL)showsLineMetrics {
return _flags.showsLineMetrics;
}
- (void)setShowsLineMetrics:(BOOL)show {
_flags.showsLineMetrics = show ? 1 : 0;
}
@dynamic dimsSubstitutedGlyphs;
- (BOOL)dimsSubstitutedGlyphs {
return _flags.dimsSubstitutedGlyphs;
}
- (void)setDimsSubstitutedGlyphs:(BOOL)dim {
_flags.dimsSubstitutedGlyphs = dim ? 1 : 0;
}
@end
這段代碼是官方提供的范例甜孤,對于我們繪制弧形文字已經足夠使用,在此不做過多分析畏腕,我們只關注頭文件中提供的實例方法即可:
- (instancetype)initWithFrame:(CGRect)frame font:(UIFont *)font text:(NSString *)text radius:(float)radius arcSize:(float)arcSize color:(UIColor *)color;
通過調用這個方法我們就可以繪制出有弧形效果的文字了缴川,另外還可以為文字修改字體、內容描馅、弧度半徑把夸、張角、顏色等铭污,這里我們繪制多個文字進行對比來了解各個參數的意義恋日,尤其是弧度半徑radius和張角arcSize兩個參數
接下來我們將微調源代碼的這一行:
CGContextTranslateCTM(context, CGRectGetMidX(rect)+_shiftH, CGRectGetMidY(rect)+_shiftV - self.radius / 2.0);
這里是在移動參考上下文的原點的位置膀篮,可以看出原點的y值還和要繪制的弧度半徑相關,我們希望將原點的位置固定(只取決于底版view的尺寸)岂膳,不受要繪制的文字的參數影響誓竿,因此我們需將代碼修改為:
CGContextTranslateCTM(context, CGRectGetMidX(rect)+_shiftH, CGRectGetMidY(rect)+_shiftV);
這樣就可以了,具體效果后面我們會看到
準備工作就緒谈截,我們將開始繪制多個弧形文字來看下效果和使用方法是怎樣的
- 首先我們創(chuàng)建一個作為背景的UIView對象筷屡,這個view的尺寸決定了弧形文字視圖的尺寸,view的高度和弧度半徑radius有關簸喂,view的寬度和張角arcSize有關毙死,詳細解釋后面會提到
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *arcBgView = [[UIView alloc] init];
arcBgView.bounds = CGRectMake(0, 0, 300, 600);
arcBgView.center = self.view.center;
arcBgView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:arcBgView];
}
- 接下來,在要繪制文字的文件中引入CoreTextArcView.h頭文件喻鳄,并調用接口創(chuàng)建CoreTextArcView實例并添加就可以了扼倘,例如
CoreTextArcView *arcViewTest = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑200張角45" radius:200 arcSize:45 color:[UIColor redColor]];
arcViewTest.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcViewTest];
這樣就添加了一個弧形文字,效果如下诽表,注意黃色區(qū)域為高600寬300唉锌,圓弧的中心此時位于黃色區(qū)域的正中心,弧度半徑為200竿奏,略小于高的一半300
- 一條文字可能不足以對比出效果袄简,我們固定張角,調整半徑泛啸,繪制多條文字
CoreTextArcView *arcView1 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度0張角30" radius:0 arcSize:30 color:[UIColor redColor]];
arcView1.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView1];
CoreTextArcView *arcView2 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度50張角30" radius:50 arcSize:30 color:[UIColor redColor]];
arcView2.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView2];
CoreTextArcView *arcView3 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度100張角30" radius:100 arcSize:30 color:[UIColor redColor]];
arcView3.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView3];
CoreTextArcView *arcView4 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度150張角30" radius:150 arcSize:30 color:[UIColor redColor]];
arcView4.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView4];
CoreTextArcView *arcView5 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度200張角30" radius:200 arcSize:30 color:[UIColor redColor]];
arcView5.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView5];
CoreTextArcView *arcView6 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑250張角30" radius:250 arcSize:30 color:[UIColor redColor]];
arcView6.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView6];
CoreTextArcView *arcView7 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑300張角30" radius:300 arcSize:30 color:[UIColor redColor]];
arcView7.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView7];
這里我們看到幾個問題:
第一绿语,最上面一條半徑為300的文字已經超出了邊框外,這是因為CoreTextArcView在繪制文字時所使用的半徑radius候址,其實是在文字下方首先繪制一條弧線吕粹,再將文字貼合弧線外側繪制,因此本圖中當radius已經和黃色區(qū)域高度的一半相等時(這里都為300)岗仑,弧線已經貼到了邊框頂部匹耕,文字自然就被繪制到邊框外了,因此在繪制時我們要注意控制radius略小于設定的frame的高度的一半即可
第二荠雕,我們使用了一個很大的邊框稳其,但是圖形只繪制到了邊框內部的上半部分,那么下半部分是不是沒有用呢炸卑?答案并非如此既鞠,因為除了上弧形的文字,我們還可以繪制下弧形的文字盖文,而使用的方法是相似的嘱蛋,只需要將radius和arcSize都變成負值即可
CoreTextArcView *arcView1 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑0張角45" radius:-0 arcSize:-45 color:[UIColor redColor]];
arcView1.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView1];
CoreTextArcView *arcView2 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑50張角45" radius:-50 arcSize:-45 color:[UIColor redColor]];
arcView2.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView2];
CoreTextArcView *arcView3 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑100張角45" radius:-100 arcSize:-45 color:[UIColor redColor]];
arcView3.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView3];
CoreTextArcView *arcView4 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑150張角45" radius:-150 arcSize:-45 color:[UIColor redColor]];
arcView4.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView4];
CoreTextArcView *arcView5 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑200張角45" radius:-200 arcSize:-45 color:[UIColor redColor]];
arcView5.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView5];
CoreTextArcView *arcView6 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑250張角45" radius:-250 arcSize:-45 color:[UIColor redColor]];
arcView6.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView6];
CoreTextArcView *arcView7 = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑300張角45" radius:-300 arcSize:-45 color:[UIColor redColor]];
arcView7.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcView7];
這里我們看到最外層radius為-300的文字沒有繪制到邊框外,因為負值半徑改變了弧形文字貼合弧形圖的方向,也就將文字繪制到了弧形內側洒敏,這里我們可以通過再繪制一個正半徑和一個負半徑全角度的文字來說明:
//藍色的是正半徑龄恋,外圈繪制
CoreTextArcView *arcViewOutFull = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑150張角360弧度半徑150張角360弧度半徑150張角360" radius:150 arcSize:360 color:[UIColor blueColor]];
arcViewOutFull.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcViewOutFull];
//紅色的是負半徑,內圈繪制
CoreTextArcView *arcViewInFull = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑-150張角360弧度半徑-150張角360弧度半徑-150張角360" radius:-150 arcSize:360 color:[UIColor redColor]];
arcViewInFull.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcViewInFull];
這樣我們就能看出來正負半徑帶來的繪制差異了桐玻,也就是半徑正值或負值將決定文字要繪制到弧形圖的外側或者內側篙挽,另外也可以看出,arcSize是用來決定文字展開的角度有多大的镊靴,文字將會根據這個角度自動填充間隙铣卡,arcSize如果為負值時,也會影響文字的排列順序偏竟,所以需要結合半徑是正值還是負值來使用才可以滿足不同的需求
到這里為止煮落,利用CoreTextArcView來繪制弧形文字的基本方法就說的差不多了,總結起來步驟大概是這樣的:
- 添加CoreTextArcView文件(可以是其他名稱)和代碼(根據個人喜好決定是否修改源代碼來增強使用效率)
- 添加一個view作為繪制弧形文字的背景底版踊谋,弧形的中心將是該view的中心點蝉仇,view的寬高設定一定要符合弧形文字的尺寸需要(寬度對應文字的張角arcSize,高度的一半對應文字的弧度半徑radius)
- 調用接口-initWithFrame:font:radius:arcSize:color: 來創(chuàng)建和配置所需要的弧形文字(注意半徑和張角的正值或負值將決定文字的繪制方式和樣式)
- 將創(chuàng)建好的弧形文字視圖的背景色設為透明色(否則會以餅狀形式覆蓋住圓心到文字之間的區(qū)域V巢稀)
- 添加到底版view上顯示即可
多說兩句
在文章開始我們提到過通過修改源代碼使得調用接口進行調試的結果變得簡單直觀轿衔,這只是個人喜好,本人沒有仔細鉆研過蘋果提供的源代碼睦疫,只是通過修改的方式找到了符合自己的更好使用的方法害驹,也就是在設置好參數后,腦海中就已經出現大致的圖形了蛤育,在使用源代碼時這個部分因為添加了self.radius的成分而使得半徑的變化摻雜了多余的影響因素宛官,對判斷位置有一些干擾,故而做出修改
前面提到過瓦糕,如果我們只繪制一半弧形文字時底洗,會浪費掉一半底版區(qū)域,也會給設置底版view的位置帶來一些麻煩咕娄,因此我們可以進一步修改亥揖,使得源代碼能夠在繪制上弧形文字時,將弧形中心移到view底部中心圣勒,在繪制下弧形文字時徐块,將弧形中心移到view頂部中心,這樣既可以不必創(chuàng)建過大的底版view灾而,也可以在將view添加到其他view上進行組合時,能更好的以弧形圓心為參照進行調整
這里我們整理三種繪制方式需要修改的代碼細節(jié):
// 弧形圓心位于底版view正中心扳剿,適用于所有繪制情況旁趟,尤其是弧形文字張角大于180度的情況(比如上面繪制一整個圓形)或者弧形文字跨越水平線(左弧形或右弧形)
CGContextTranslateCTM(context, CGRectGetMidX(rect)+_shiftH, CGRectGetMidY(rect)+_shiftV);
// 弧形圓心位于底版view底部中心,適用于上弧形
CGContextTranslateCTM(context, CGRectGetMidX(rect)+_shiftH, _shiftV);
// 弧形圓心位于底版view頂部中心,使用與下弧形
CGContextTranslateCTM(context, CGRectGetMidX(rect)+2*CGRectGetMidY(rect)_shiftH, -_shiftV);
我們添加一段測試代碼來檢測一下锡搜,首先是上弧形文字橙困,注意半徑略小于view的高度才能完整顯示
CoreTextArcView *arcViewTopTest = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑280張角45" radius:280 arcSize:45 color:[UIColor redColor]];
arcViewTopTest.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcViewTopTest];
接下來是下弧形文字
CoreTextArcView *arcViewBottomTest = [[CoreTextArcView alloc] initWithFrame:arcBgView.bounds font:[UIFont systemFontOfSize:20] text:@"弧度半徑300張角45" radius:-300 arcSize:-45 color:[UIColor redColor]];
arcViewBottomTest.backgroundColor = [UIColor clearColor];
[arcBgView addSubview:arcViewBottomTest];
簡單的變換使用就是這些了,除此之外還有其他很多種變化方法可以嘗試
【參考資料】
Github - Apple CoreText Framework CoreTextArcView
https://github.com/alokc83/iOS-Example-Collections/tree/master/WWDC_2012_SourceCode/iOS/226%20-%20Core%20Text%20and%20Fonts/CoreTextArcCocoa
Github - XMCircleType
https://github.com/MichMich/XMCircleType
stack overflow - Curve text on existing circle
http://stackoverflow.com/questions/3841642/curve-text-on-existing-circle/7114184#7114184