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