iOS 円や, 角丸View をつくる

もうひとつのプロフェッショナルプログラマーより転載です。
1年ほど前に個人でリリースしたソフトウェアで使ったテクニックです。

UIView を使って, いろいろな形を作って, それを画面に張り付けたりして, かっこいいUIをつくりたいわけですが, その中で, 円と角丸のView を作ってみました。
成果物はこちら, ColorDos というソフトです。TODOアプリです。 4ヶ国語対応, 無料です。

Roundrect

方法は2つあります。 ColorDos のVersion1.0 では, 上を 1.1 は下を使っています。

  • QuartzCore.framework  を使って view.layer.cornerRadius で角を丸める
  • Core Graphics,  UIView の drawRect で, Shape を描画する

方法があります。
率直に言いましょう。 1 は極めて簡単です。 丸も工夫すればたったの2行で書けちゃいます。
ですが, これを多用すると, ものすごい遅いです。
※ちなみに筆者は, カレンダーで, 30個くらい描いてみましたが, ちょっと気になるくらいかくかくしています。
2を使うと, 非常にスムーズです。 ですが, 多少コードに工夫は必要です。(Core Graphicsの描画を使います)
では早速 1から行きましょう。

QuartzCore の view.layer.conerRadius を使う

まず, QuartzCore.framework をリンクしましょう
“Project”をフォーカス -> “TARGETS” -> “Linked Frameworks and Libralies” -> QuartzCore を探す
ヘッダファイルに, を入れましょう
UIViewを作ります
QuartzCore は, layer というプロパティにアクセスするために用います。

角丸の場合

UIView *view = [UIView alloc] initWithFrame:CGRectmake(x,y,width, height)];
view.layer.cornerRadius = 2.0f;
view.clipsToBounds = YES;

円の場合

角丸は, cornerRadius をうまく調節すればいいのですが, 丸はどうでしょう。
実は, UIView を正方形に作った場合, その1辺の長さの半分を cornerRadius にすれば, 円になります。

UIView *view = [UIView alloc] initWithFrame:CGRectmake(x,y,width, width)];
view.layer.cornerRadius = width / 2.0;
view.clipsToBounds = YES;

Core Graphics, UIView の drawRect で, Shape を描画する

さて1の方法では, 大量に描画すると, あっという間にメモリを使い切って, かくかくとした感じになります。(一体中でどんな動作をしているのでしょう)
そこで, Core Graphics を丁寧に使って, UIView の拡張クラスをつくり, 自作のView を作りましょう。
ここでも, QuartzCore さんを使うので, 1と同じ要領で設定してください

.h

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface UICircleView : UIView

@property (nonatomic) UIColor *color;

-(id)initWithFrameColor:(CGRect)frame color:(UIColor *)color;

@end

.m

#import "UICircleView.h"

@implementation UICircleView
@synthesize color;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (id)initWithFrameColor:(CGRect)frame color:(UIColor *)refcolor
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.color = refcolor;
    }
    return self;
}

-(void)drawRect:(CGRect)rect
{
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGFloat w = self.bounds.size.width;
    CGFloat h = self.bounds.size.width;
    CGContextSetFillColorWithColor(c, self.color.CGColor);
    CGContextFillEllipseInRect(c, CGRectMake(0, 0, w, h));
}

使い方

UIView *colorRect = [[UICircleView alloc] initWithFrameColor:rect color:color];
colorRect.backgroundColor = [UIColor clearColor];

※rect は, Frame CGMake か何かで作ってください, color は UIColor のインスタンスです.
ちなみにbackground は, clearColorにしとくと, 上の画像みたいにぴっちり, 丸だけになります。

続いて角丸です。角丸は若干難しいです。

角丸

.m

#import "UIRoundRectView.h"
@implementation UICircleView
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (id)initWithFrameColor:(CGRect)frame color:(UIColor *)refcolor
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.color = refcolor;
    }
    return self;
}

-(void)drawRect:(CGRect)rect
{
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGFloat w = self.bounds.size.width;
    CGFloat h = self.bounds.size.width;
    CGFloat r = w * 0.20;
    CGContextSetFillColorWithColor(c, self.color.CGColor);
    
    CGRect rc = CGRectMake(0, 0, w, h);
    CGContextMoveToPoint(c, CGRectGetMinX(rc), CGRectGetMaxY(rc)-r);
    CGContextAddArcToPoint(c, CGRectGetMinX(rc), CGRectGetMinY(rc), CGRectGetMidX(rc), CGRectGetMinY(rc), r);
    CGContextAddArcToPoint(c, CGRectGetMaxX(rc), CGRectGetMinY(rc), CGRectGetMaxX(rc), CGRectGetMidY(rc), r);
    CGContextAddArcToPoint(c, CGRectGetMaxX(rc), CGRectGetMaxY(rc), CGRectGetMidX(rc), CGRectGetMaxY(rc), r);
    CGContextAddArcToPoint(c, CGRectGetMinX(rc), CGRectGetMaxY(rc), CGRectGetMinX(rc), CGRectGetMidY(rc), r);
    CGContextClosePath(c);
    CGContextFillPath(c);
}

@end

使い方は上と同じ形です。
こちらは, r というパラメータで, 丸くなる部分を調節しています。これもパラメータで渡しとけばよかった。
さて, これで描画したら, パフォーマンスが無茶苦茶上がりました。 手抜きをせず頑張るべきですね。