今回は、少しこだわったテキストを表示したい時に重宝する拡張クラスについてのご紹介です。
iPhone/iPad/iPodアプリ開発において、テキストを表示する際には
きっとUIKitのUILabelを使用して表示しているかと思います。
しかし、このままだとテキストのフォントを変えたり、色を変えたりする位しかできません。
そんなUILabelのテキストにちょっとしたアレンジを加えたいと思います。
UILabelを拡張しよう!
今回は、テキストを表示するUILabelを拡張して、
テキスト部分に縁取りをつけて見たいとおもいます。
通常ですと、縁取りは専用の画像を作成して使っている人も多いはずです。
しかし、画像の様に固定要素になると、テキストの変更の度に画像を修正する必要がありますし、
動的に変わってしまうテキストへは対応する事が出来ません。
しかし、UILabelを拡張すれば、UILabelへセットしたテキストを縁取る事が可能になります。
コレはとても便利ですね。
それでは、早速そんな縁取りクラスをUILabelを拡張して作成しましょう。
まずは、UILabelを拡張する為に、UILabelを継承したクラスを作成します。
実装サンプルは下記を参考にして下さい。
#import#import @interface UIEdgeLabel : UILabel { // 縁取りの色 UIColor* outlineColor; // 縁取りの幅 NSInteger outlineWidth; // 縁取りのオフセット NSInteger outlineOffsetX; NSInteger outlineOffsetY; } @property (nonatomic,retain) UIColor* outlineColor; @property (nonatomic,assign) NSInteger outlineWidth; @property (nonatomic,assign) NSInteger outlineOffsetX; @property (nonatomic,assign) NSInteger outlineOffsetY; // UILabelをUIImageとして取得する -(UIImage*)getLabelImage; @end
UILabelを継承したUIEdgeLabelという縁取り拡張用のクラスを作成します。
上記コードは.hファイルの中身です。
縁取りに対する設定を行う為に、縁取りの色や縁取りの幅、縁取りのオフセットの値を
メンバに保持し、プロパティ宣言を行っています。
そして、縁取りしたUILabelをUIImageとして取得する専用メソッドを宣言しています。
縁取りの処理は少し重たいので、tableView等で、そのまま使用すると
処理が追いつかない可能性があります。
そこで、UIImageへ変換して保持し、出来上がったイメージとして貼り付ける事で
無駄な処理を毎回走らせる事無く、軽快に描画される様になります。
それでは、続きまして実装ファイルです。
#import "UIEdgeLabel.h" @implementation UIEdgeLabel @synthesize outlineColor; @synthesize outlineWidth; @synthesize outlineOffsetX; @synthesize outlineOffsetY; //------------------------------------------------------------------------------ // 枠を作成する //------------------------------------------------------------------------------ -(void)drawTextInRect:(CGRect)rect { CGSize shadowOffset = self.shadowOffset; UIColor* textColor = self.textColor; CGContextRef c = UIGraphicsGetCurrentContext(); // アウトライン部分 // 縁取りの幅を指定する CGContextSetLineWidth( c, self.outlineWidth ); CGContextSetLineJoin( c, kCGLineJoinRound ); CGContextSetTextDrawingMode( c, kCGTextStroke ); // 縁取りの色を指定する self.textColor = self.outlineColor; // オフセットを指定して描く [super drawTextInRect:CGRectInset(rect, self.outlineOffsetX, self.outlineOffsetY )]; // 既存のRECT CGContextSetTextDrawingMode( c, kCGTextFill ); self.textColor = textColor; self.shadowOffset = CGSizeMake( 0, 0 ); [super drawTextInRect:CGRectInset(rect, self.outlineOffsetX, self.outlineOffsetY )]; self.shadowOffset = shadowOffset; } //------------------------------------------------------------------------------ // UILabelをUIImageとして取得する //------------------------------------------------------------------------------ -(UIImage*)getLabelImage { // Retina対応の為、全て倍のサイズにする NSInteger width = self.frame.size.width*2; NSInteger height = self.frame.size.height*2; self.frame = CGRectMake( 0, 0, width, height); // フォントタイプを判別する NSRange range = [self.font.fontName rangeOfString:@"Bold"]; if( range.location != NSNotFound ){ // Boldとしてフォントサイズを二倍にする self.font = [UIFont boldSystemFontOfSize:(self.font.pointSize*2)]; } else{ // フォントサイズを二倍にする self.font = [UIFont boldSystemFontOfSize:(self.font.pointSize*2)]; } // コンテキストを作成 UIGraphicsBeginImageContext( CGSizeMake( width, height ) ); // コンテキストに描画する [self drawRect:CGRectMake( 0, 0, width, height )]; // UIImageにコンテキスト内容を保存し、コンテキストを閉じる UIImage* image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // imageの作成が終了したので、元のサイズに戻しておく self.font = [UIFont systemFontOfSize:(self.font.pointSize/2)]; width = self.frame.size.width/2; height = self.frame.size.height/2; self.frame = CGRectMake( 0, 0, width, height); return( image ); } @end
まず初めに、-(void)drawTextInRect:(CGRect)rectメソッドについての説明です。
-(void)drawTextInRect:(CGRect)rectは、UILabel内に元々あるメソッドです。
今回は、そんなメソッドをオーバーライド(上書き)して拡張します。
それでは、drawTextInRect内で、通常の処理の前に行っている重要な設定部分を解説します。
コンテキストを取得します
CGContextRef c = UIGraphicsGetCurrentContext();
縁取りの太さを指定します
CGContextSetLineWidth( c, self.outlineWidth );
縁取りの色を設定します
self.textColor = self.outlineColor;
描く位置をオフセット指定して描きます
[super drawTextInRect:CGRectInset(rect, self.outlineOffsetX, self.outlineOffsetY )];
初めに、少し大きめにテキストを色指定で描き、その後に通常サイズのテキストを描く事で、
はみ出た部分が縁取りとして表現されます。
UILabelが描画される時に自動で呼び出されますので、
今回拡張したdrawTextInRectは自分で呼び出す必要はありません。
続きまして、そんな縁取りを実行したものを使いまわせる様にUIImageへ変換して、
返却する独自メソッド-(UIImage*)getLabelImageについて解説します。
処理の流れと致しましては、
コメントにも有るとおり、まずはRetina対応させる為にUILabel自身の領域サイズを倍にします。
そして、テキストも倍にする必要があるので、boldスタイルなのかを判定した後に、
フォントサイズを倍にします。
UILabelの内容を書き出す為のコンテキストを作成して、
作成したコンテキストへ描画します。
(この際、先程オーバーライドしたメソッドが実行され、縁取りの有るテキストがコンテキストに対して描画される)
描画されたコンテキストの内容をUIImageに保存して、コンテキスト事態は閉じてしまいます。
保存されたUIImageも倍のサイズになっているので、通常のサイズへ戻してあげた後に
作成されたUIImageを返却します。
UILabel縁取り拡張クラスの使い方
先程の説明で作成したUILabelを拡張した縁取りクラスを実際に使用して見ましょう。
基本的にUILabelを継承していますので、使用方法は通常のUILabelと同じです。
そこへ拡張した機能を使える様になってという事で、拡張されたメンバへ値をセットします。
実際の使用サンプルは下記を参考にして下さい。
// UILabel縁取り拡張クラスをアロケート UIEdgeLabel* title_label = [[[UIEdgeLabel alloc] init] autorelease]; // 通常のUILabelと同じ様に要素を設定していく title_label.frame = CGRectMake( 0, 0, 230, 20 ); title_label.textAlignment = UITextAlignmentLeft; title_label.baselineAdjustment = UIBaselineAdjustmentAlignCenters; title_label.font = [UIFont boldSystemFontOfSize:18]; title_label.backgroundColor = [UIColor clearColor]; title_label.textColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]; title_label.shadowColor = [UIColor colorWithRed:0.4f green:0.2f blue:0.0f alpha:0.5f]; title_label.shadowOffset = CGSizeMake( 1.0f, 1.0f ); title_label.text = @"縁取りしたいテキスト"; title_label.adjustsFontSizeToFitWidth = YES; title_label.minimumFontSize = 12; // ここから拡張プロパティ // 縁取りの色を設定する title_label.outlineColor = [UIColor whiteColor]; // 縁取りの幅を指定する title_label.outlineWidth = 4; // 縁取りの表示位置をオフセットで指定する title_label.outlineOffsetX = 1; title_label.outlineOffsetY = 1; // ラベルをImageに変換して取得 UIImage* label_img = [title_label getLabelImage];
これで簡単にテキストを縁取り付きで表示出来るようになります。
今回のサンプルでは、汎用的にクラス化していますので、
ライブラリ的に使用して頂ければ幸いです。
また、導入して頂ける方はコメントを残して頂けたら
嬉しいですし、今後の励みになりますので、気が向いたらコメントを残して下さいね
以上、UILabelを拡張してテキストに縁取りを付ける方法でした。