CAGradientLayer を使ったグラデーション付きボタンの試作

2011年10月22日土曜日 | Published in | 4 コメント

このエントリーをはてなブックマークに追加

CAGradientLayer を使った描画がしたくて調べている。試しにボタンを作ってみた。


仕組み


UIControl をベースのクラスに使い、その上にレイヤーを重ねて作ってある。
基本の色は UIControl.backgroundColor で決める。

その上に CAGradientLayer をかぶせてグラデーションをかける。グラデーションは白色をアルファ値を変えて表現する。
self.gradientLayer = [CAGradientLayer layer];
    self.gradientLayer.frame = self.bounds;
    self.gradientLayer.locations = [NSArray arrayWithObjects:
                                    [NSNumber numberWithFloat:0.0],
                                    [NSNumber numberWithFloat:0.5],
                                    [NSNumber numberWithFloat:0.5],
                                    [NSNumber numberWithFloat:1.0],
                                    nil]
     self.gradientLayer.colors =
        [NSArray arrayWithObjects:
             (id)[UIColor colorWithWhite:1.0 alpha:0.7].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.4].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.3].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.0].CGColor,
             nil];
    [self.layer addSublayer:self.gradientLayer];
こうするとベースの色(UIControl.backgroundColor)は単純に基本の色を指定するだけで良い。色を加工したり、あらかじめ色のグラーでションを用意する必要が無いので使い勝手がいい(と思う)。

その上にボタンの文字を CATextLayer に描く。
self.textLayer = [CATextLayer layer];
    self.textLayer.string = self.text;
    self.textLayer.font = CGFontCreateWithFontName((CFStringRef)[self _font].fontName);
    self.textLayer.fontSize = [self _font].pointSize;
    self.textLayer.truncationMode = kCATruncationEnd;
    self.textLayer.alignmentMode = kCAAlignmentCenter;
    self.textLayer.shadowColor = [UIColor blackColor].CGColor;
    self.textLayer.shadowRadius = 0.5;
    self.textLayer.shadowOffset =CGSizeMake(-0.5, -0.5);
    self.textLayer.shadowOpacity = 0.5;
    self.textLayer.foregroundColor = [UIColor whiteColor];
    [self.layer addSublayer:self.textLayer];

その上のレイヤーはボタンに立体感を出すためのハイライトライン。
これを描くために CALayer のサブクラス HighlightEdgeLayer を作り自前で線を描画している。
#define CORNER_RADIUS   5.0

@implementation HighlightEdgeLayer
- (void)drawInContext:(CGContextRef)context
{
    CGMutablePathRef path = CGPathCreateMutable();
    CGPoint p = CGPointMake(CORNER_RADIUS, CORNER_RADIUS);

    CGPathAddArc(path, NULL,
                 p.x, p.y,
                 CORNER_RADIUS,
                 2.0*M_PI/2.0,
                 3.0*M_PI/2.0,
                 false);

    p.x = self.bounds.size.width - CORNER_RADIUS;
    CGPathAddArc(path, NULL,
                 p.x, p.y,
                 CORNER_RADIUS,
                 3.0*M_PI/2.0,
                 4.0*M_PI/2.0,
                 false);

    CGContextAddPath(context, path);
    CGPathRelease(path);
    CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:1.0 alpha:0.5].CGColor);
    CGContextSetLineWidth(context, 1.0);
    CGContextDrawPath(context, kCGPathStroke);

}
@end
一番上のレイヤーは UIControl に最初からついている CALayer。これで黒い境界線を引き、角を丸くしている。
self.layer.cornerRadius = CORNER_RADIUS;
    self.layer.masksToBounds = YES;
    self.layer.borderColor = [UIColor colorWithWhite:0.0 alpha:0.75].CGColor;
    self.layer.borderWidth = 1.0;

ボタンが押された時には -[UIControl setHighlighted:] が呼ばれるのでここでグラデーションの色を変えてやる。
- (void)setHighlighted:(BOOL)highlighted
{
    [super setHighlighted:highlighted];
    [self _setState:highlighted ? GradientButtonStateHighlighted : GradientButtonStateNormal];
}
サンプルでは3つの状態を定義しておいて、それぞれでグラデーションの色と文字色を変えている。
typedef enum {
    GradientButtonStateNormal = 0,
    GradientButtonStateHighlighted,
    GradientButtonStateDisabled
} GradientButtonState;

- (void)_setState:(GradientButtonState)state
{
    switch (state) {
        case GradientButtonStateNormal:
            self.gradientLayer.colors =
            [NSArray arrayWithObjects:
             (id)[UIColor colorWithWhite:1.0 alpha:0.7].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.4].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.3].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.0].CGColor,
             nil];
            self.textLayer.foregroundColor = self.textColor.CGColor;
            break;
            
        case GradientButtonStateHighlighted:
            self.gradientLayer.colors =
            [NSArray arrayWithObjects:
             (id)[UIColor colorWithWhite:1.0 alpha:0.5].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.2].CGColor,
             (id)[UIColor colorWithWhite:0.0 alpha:0.05].CGColor,
             (id)[UIColor colorWithWhite:0.0 alpha:0.1].CGColor,
             nil];
            self.textLayer.foregroundColor = self.textColor.CGColor;
            break;
            
        case GradientButtonStateDisabled:
            self.gradientLayer.colors =
            [NSArray arrayWithObjects:
             (id)[UIColor colorWithWhite:1.0 alpha:0.7].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.4].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.3].CGColor,
             (id)[UIColor colorWithWhite:1.0 alpha:0.0].CGColor,
             nil];
            self.textLayer.foregroundColor = [UIColor lightGrayColor].CGColor;
            break;
    }
}
押す前

押した後
ディゼーブル


サンプル


何色かのボタンを並べたサンプルを作ってみた。最後のボタンは Disable状態。
ベースの色は階調が見やすいように濃いめの色にした方がいい。これはグラデーションの表現を白で表現している為。標準で UIColor に用意されている greenColor や yellowColor は明るすぎて階調が出づらい。

Xib はこんな感じ。UIView を配置して大きさを調整した後、クラスに GradientButton を指定する。


利用上の注意点としては Xib 上で IBAction を指定すると Value Changed のアクションがデフォルトで選択される。これではボタン押下時のイベントが取れないので Touch up inside を使う。


ソースコード


GitHub からどうぞ。
GradientButton at 2011-10-22 from xcatsan/iOS-Sample-Code - GitHub


参考情報


テン*シー*シー - 【iPhoneアプリ開発ドリル】Aqua風ボタンを作る

CAGradientLayer の使い方はこのサイトがとても参考になった。UIControl をボタンとして実装するアイディアはここから拝借した。情報をどうも!

iPhoneアプリ開発、その(17) 文字だって回転|テン*シー*シー

CATextLayer の使い方もここ。

iphone - How can i convert UIFont to CGFontRef?I causes warning and not working - Stack Overflow

UIFont から CGFontRef を取る方法。
CGFontRef cgFont = CGFontCreateWithFontName((CFStringRef)uiFont.fontName);

サンプルコードのボタンのグラデーション(ハイライト)は iOS のポップアップメニューを参考にした。


なので通常のボタンと見た目がちょっと違う。


関連情報


CATextLayer Class Reference
CAGradientLayer Class Reference


Responses

  1. 匿名
    2012年6月19日 9:33

    素晴らしいソースコード、どうも有難う御座います。
    このコードを使わせて頂く場合、従うライセンスや、表記すべき Acknowledgments はありますでしょうか?

  2. 匿名
    2012年6月19日 9:33

    素晴らしいソースコード、どうも有難う御座います。
    このコードを使わせて頂く場合、従うライセンスや、表記すべき Acknowledgments はありますでしょうか?

  3. xcatsan says:
    2012年6月20日 13:03

    こんにちは。

    ソースコードのライセンスは MITになります。

    が、表記はなくても構いません。
    もし何か表記する場合でもソースコードが置いてあるgithubのURLでも載せてもらえれば大丈夫です。

    では。

  4. xcatsan says:
    2012年6月20日 13:03

    こんにちは。

    ソースコードのライセンスは MITになります。

    が、表記はなくても構いません。
    もし何か表記する場合でもソースコードが置いてあるgithubのURLでも載せてもらえれば大丈夫です。

    では。

  5. 匿名
    2012年6月20日 14:56

    有難うございます。ライセンスの件了解いたしました。
    自由度の高いライセンスで、非常に有り難いです。

  6. 匿名
    2012年6月20日 14:56

    有難うございます。ライセンスの件了解いたしました。
    自由度の高いライセンスで、非常に有り難いです。

  7. 匿名
    2013年1月16日 19:26

    こんにちは。
    大変勉強になりました。

    Use AutolayoutをOFFにすると問題ありませんが、
    ONにするとボタンが表示されませんでした。
    ご参考までに。(xcode 4.5、iOS 6.0)

  8. 匿名
    2013年1月16日 19:26

    こんにちは。
    大変勉強になりました。

    Use AutolayoutをOFFにすると問題ありませんが、
    ONにするとボタンが表示されませんでした。
    ご参考までに。(xcode 4.5、iOS 6.0)

Leave a Response

人気の投稿(過去 30日間)