2012年10月4日木曜日

iOSで, 円や, 角丸View をつくる

こんにちは, またしばらくWeb系開発にいそしんでおりまして, iOSが触れない日々が続いています。

先日, アプリの手直しをして ようやくupdate がかかりましたので, この内容が書けるようになりました。


UIView を使って, いろいろな形を作って, それを画面に張り付けたりして, かっこいいUIをつくりたいわけですが, その中で, 円と角丸のView を作ってみました。


成果物はこちら, ColorDos というソフトです。TODOアプリです。 4ヶ国語対応, 無料ですので, 今回のコードでどんなものができるの確認したい方は是非是非 よろしくです。(やっぱり, 最近は出してもDownload数伸びないなぁ~)




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


1.  QuartzCore.framework  を使って view.layer.cornerRadius で角を丸める

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


方法があります。
率直に言いましょう。 1 は極めて簡単です。 丸も工夫すればたったの2行で書けちゃいます。
ですが, これを多用すると, ものすごい遅いです。

※ちなみに筆者は, カレンダーで, 30個くらい描いてみましたが, ちょっと気になるくらいかくかくしています。

2を使うと, 非常にスムーズです。 ですが, 多少コードに工夫は必要です。

では早速 1から行きましょう。


1.  QuartzCore の view.layer.conerRadius を使う


・ まず, QuartzCore.framework をリンクしましょう

"Project"をフォーカス -> "TARGETS" -> "Linked Frameworks and Libralies" -> QuartzCore を探す

・ ヘッダファイルに, <QuartzCore/QuartzCore.h> を入れましょう

・ UIViewを作ります

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


QuartzCore は, この layer というプロパティにアクセスするために用います。


※UIView の * が抜けていました。ご指摘ありがとうございます。



● 丸の書き方
角丸は, cornerRadius をうまく調節すればいいのですが, 丸はどうでしょう。

実は, UIView を正方形に作った場合, その1辺の長さの半分を cornerRadius にすれば, 円になります。


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


※ここで注目するのは, とくに, UIViewを拡張したり, あたらしいクラスを作ったりしていないところです。 操作したのはプロパティだけです



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


さて1の方法では, 大量に描画すると, あっという間にメモリを使い切って, かくかくとした感じになります。(一体中でどんな動作をしているのでしょう)

そこで, Core Graphics を丁寧に使って, UIView の拡張クラスをつくり, 自作のView を作りましょう。

ここでも, QuartzCore さんを使うので, 1と同じ要領で設定してください


まず円から行きましょう。
ex)  UICircleView.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));
}

@end


ex) UICircleView.h

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

@interface UICircleView : UIView

@property (nonatomic) UIColor *color;

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

@end


※使い方


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

※rect は, Frame CGMake か何かで作ってください, color は UIColor のインスタンスです.



ちなみにbackground は, clearColorにしとくと, 上の画像みたいにぴっちり, 丸だけになります。


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

ex)UIRectView.m   (これもUIViewの拡張です)


- (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 というパラメータで, 丸くなる部分を調節しています。これもパラメータで渡しとけばよかった。

さて, これで描画したら, パフォーマンスが無茶苦茶上がりました。 手抜きをせず頑張るべきですね。







2 件のコメント:

  1. とても参考になりました。ありがとうございます。
    使い方の
    UIView colorRect = [[UICircleView alloc] initWithFrameColor:rect color:color];

    UIView *colorRect = [[UICircleView alloc] initWithFrameColor:rect color:color];
    正しくはこうですよね。

    返信削除
  2. はいその通りです。UIViewのところの * がちょくちょく抜けていました。ご指摘ありがとうございます。

    返信削除