仕組み
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














匿名
2012年6月19日 9:33
素晴らしいソースコード、どうも有難う御座います。
このコードを使わせて頂く場合、従うライセンスや、表記すべき Acknowledgments はありますでしょうか?
匿名
2012年6月19日 9:33
素晴らしいソースコード、どうも有難う御座います。
このコードを使わせて頂く場合、従うライセンスや、表記すべき Acknowledgments はありますでしょうか?
xcatsan says:
2012年6月20日 13:03
こんにちは。
ソースコードのライセンスは MITになります。
が、表記はなくても構いません。
もし何か表記する場合でもソースコードが置いてあるgithubのURLでも載せてもらえれば大丈夫です。
では。
xcatsan says:
2012年6月20日 13:03
こんにちは。
ソースコードのライセンスは MITになります。
が、表記はなくても構いません。
もし何か表記する場合でもソースコードが置いてあるgithubのURLでも載せてもらえれば大丈夫です。
では。
匿名
2012年6月20日 14:56
有難うございます。ライセンスの件了解いたしました。
自由度の高いライセンスで、非常に有り難いです。
匿名
2012年6月20日 14:56
有難うございます。ライセンスの件了解いたしました。
自由度の高いライセンスで、非常に有り難いです。
匿名
2013年1月16日 19:26
こんにちは。
大変勉強になりました。
Use AutolayoutをOFFにすると問題ありませんが、
ONにするとボタンが表示されませんでした。
ご参考までに。(xcode 4.5、iOS 6.0)
匿名
2013年1月16日 19:26
こんにちは。
大変勉強になりました。
Use AutolayoutをOFFにすると問題ありませんが、
ONにするとボタンが表示されませんでした。
ご参考までに。(xcode 4.5、iOS 6.0)