ios 带有渐变、圆角、边框和阴影的 UIButton
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5971073/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UIButton w/ gradient, rounded corners, border, and drop shadow
提问by jinglesthula
There are a few similar questions on the site, but I'm looking for something specific and slightly different.
该网站上有一些类似的问题,但我正在寻找一些具体且略有不同的问题。
I followed the direction given here: http://www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers/to subclass UIButton in order to create a generic class that I can specify the gradient colors on, rather than trying to use a static image.
我按照这里给出的方向:http: //www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers/子类化 UIButton 以创建一个通用类,我可以指定渐变颜色,而不是尝试使用静态图像。
I ran into a problem where the setMasksToBounds on the button's layer would either allow A) the drop shadow to show, but also allow the gradient layer to show beyond the rounded corners OR B) the gradient layer to clip to the rounded corners, but not allow the drop shadow to show
我遇到了一个问题,按钮层上的 setMasksToBounds 要么允许 A) 显示阴影,但也允许渐变层显示超出圆角或 B) 渐变层剪辑到圆角,但不允许允许阴影显示
My solution to the problem seemed clunky and (although it works) I wanted to see if anyone knew of a better and/or cleaner way to accomplish the same thing. Here's my code:
我对这个问题的解决方案似乎很笨拙,(虽然它有效)我想看看是否有人知道完成同样事情的更好和/或更干净的方法。这是我的代码:
CSGradientButton.h
CSGradientButton.h
#import <UIKit/UIKit.h>
@interface CSGradientButton : UIButton {
UIColor *_highColor;
UIColor *_lowColor;
CAGradientLayer *gradientLayer;
CALayer *wrapperLayer;
CGColorRef _borderColor;
}
@property (nonatomic, retain) UIColor *_highColor;
@property (nonatomic, retain) UIColor *_lowColor;
@property (nonatomic) CGColorRef _borderColor;
@property (nonatomic, retain) CALayer *wrapperLayer;
@property (nonatomic, retain) CAGradientLayer *gradientLayer;
- (void)setHighColor:(UIColor*)color;
- (void)setLowColor:(UIColor*)color;
- (void)setBorderColor:(CGColorRef)color;
- (void)setCornerRadius:(float)radius;
@end
CSGradient.m (the interesting parts, anyway)
CSGradient.m(有趣的部分,无论如何)
#import "CSGradientButton.h"
@implementation CSGradientButton
...
- (void)awakeFromNib
{
// Initialize the gradient wrapper layer
wrapperLayer = [[CALayer alloc] init];
// Set its bounds to be the same of its parent
[wrapperLayer setBounds:[self bounds]];
// Center the layer inside the parent layer
[wrapperLayer setPosition:
CGPointMake([self bounds].size.width/2,
[self bounds].size.height/2)];
// Initialize the gradient layer
gradientLayer = [[CAGradientLayer alloc] init];
// Set its bounds to be the same of its parent
[gradientLayer setBounds:[self bounds]];
// Center the layer inside the parent layer
[gradientLayer setPosition: CGPointMake([self bounds].size.width/2,
[self bounds].size.height/2)];
// Insert the layer at position zero to make sure the
// text of the button is not obscured
[wrapperLayer insertSublayer:gradientLayer atIndex:0];
[[self layer] insertSublayer:wrapperLayer atIndex:0];
// Set the layer's corner radius
[[self layer] setCornerRadius:0.0f];
[wrapperLayer setCornerRadius:0.0f];
// Turn on masking
[wrapperLayer setMasksToBounds:YES];
// Display a border around the button
// with a 1.0 pixel width
[[self layer] setBorderWidth:1.0f];
}
- (void)drawRect:(CGRect)rect
{
if (_highColor && _lowColor)
{
// Set the colors for the gradient to the
// two colors specified for high and low
[gradientLayer setColors:
[NSArray arrayWithObjects:
(id)[_highColor CGColor],
(id)[_lowColor CGColor], nil]];
}
[super drawRect:rect];
}
- (void)setCornerRadius:(float)radius
{
[[self layer] setCornerRadius:radius];
// and get the wrapper for the gradient layer too
[wrapperLayer setCornerRadius:radius];
}
- (void)setHighColor:(UIColor*)color
{
// Set the high color and repaint
[self set_highColor:color];
[[self layer] setNeedsDisplay];
}
- (void)setLowColor:(UIColor*)color
{
// Set the low color and repaint
[self set_lowColor:color];
[[self layer] setNeedsDisplay];
}
- (void)setBorderColor:(CGColorRef)color
{
[[self layer] setBorderColor:color];
[[self layer] setNeedsDisplay];
}
@end
As you can see, I add a 'wrapper' layer that the gradient layer can safely mask to, while the top level CALayer of the button view can safely set masksToBounds = NO when the dropshadow is added. I add a setCornerRadius: method to allow both the top layer and the 'wrapper' to adjust.
如您所见,我添加了一个渐变层可以安全遮罩的“包装”层,而按钮视图的顶级 CALayer 可以在添加阴影时安全地设置 maskToBounds = NO。我添加了一个 setCornerRadius: 方法来允许顶层和“包装器”进行调整。
So rather than doing something like [[myCustomButton layer] setCornerRadius:3.0f];
I just say [myCustomButton setCornerRadius:3.0f];
As you can see, it's not maybe as clean as I'm hoping.
因此,与其像[[myCustomButton layer] setCornerRadius:3.0f];
我刚才说的[myCustomButton setCornerRadius:3.0f];
那样做一些事情,正如您所看到的,它可能不像我希望的那样干净。
Is there a better way?
有没有更好的办法?
回答by lschult2
This is the way I found to have a button with rounded corner, gradient, and drop shadow. This example has one particular gradient, but can obviously be replaced with other gradients.
这是我发现有一个带有圆角、渐变和阴影的按钮的方式。这个例子有一个特定的渐变,但显然可以用其他渐变代替。
@implementation CustomButton
- (id)initWithFrame:(CGRect)frame
{
if((self = [super initWithFrame:frame])){
[self setupView];
}
return self;
}
- (void)awakeFromNib {
[self setupView];
}
# pragma mark - main
- (void)setupView
{
self.layer.cornerRadius = 10;
self.layer.borderWidth = 1.0;
self.layer.borderColor = [UIColor colorWithRed:167.0/255.0 green:140.0/255.0 blue:98.0/255.0 alpha:0.25].CGColor;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 1;
[self clearHighlightView];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.layer.bounds;
gradient.cornerRadius = 10;
gradient.colors = [NSArray arrayWithObjects:
(id)[UIColor colorWithWhite:1.0f alpha:1.0f].CGColor,
(id)[UIColor colorWithWhite:1.0f alpha:0.0f].CGColor,
(id)[UIColor colorWithWhite:0.0f alpha:0.0f].CGColor,
(id)[UIColor colorWithWhite:0.0f alpha:0.4f].CGColor,
nil];
float height = gradient.frame.size.height;
gradient.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:0.2*30/height],
[NSNumber numberWithFloat:1.0-0.1*30/height],
[NSNumber numberWithFloat:1.0f],
nil];
[self.layer addSublayer:gradient];}
- (void)highlightView
{
self.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
self.layer.shadowOpacity = 0.25;
}
- (void)clearHighlightView {
self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f);
self.layer.shadowOpacity = 0.5;
}
- (void)setHighlighted:(BOOL)highlighted
{
if (highlighted) {
[self highlightView];
} else {
[self clearHighlightView];
}
[super setHighlighted:highlighted];
}
@end
回答by Johan Kool
Instead of inserting a gradient layer, you can also override the method +layerClass
to return the CAGradientLayer
class. The layer of the button is than of that class, and you can easily set its colors etc.
除了插入渐变层,您还可以覆盖该方法+layerClass
以返回CAGradientLayer
该类。按钮的图层比那个类的要好,你可以很容易地设置它的颜色等。
+ (Class)layerClass {
return [CAGradientLayer class];
}
回答by Matthew Huck
I had a similar problem, however instead of a gradient, I had a image for a background. I solved it in the end using:
我有一个类似的问题,但是我有一个背景图像而不是渐变。我最终使用以下方法解决了它:
+ (void) styleButton:(UIButton*)button
{
CALayer *shadowLayer = [CALayer new];
shadowLayer.frame = button.frame;
shadowLayer.cornerRadius = 5;
shadowLayer.backgroundColor = [UIColor whiteColor].CGColor;
shadowLayer.opacity = 0.5;
shadowLayer.shadowColor = [UIColor blackColor].CGColor;
shadowLayer.shadowOpacity = 0.6;
shadowLayer.shadowOffset = CGSizeMake(1,1);
shadowLayer.shadowRadius = 3;
button.layer.cornerRadius = 5;
button.layer.masksToBounds = YES;
UIView* parent = button.superview;
[parent.layer insertSublayer:shadowLayer below:button.layer];
}
The really interesting thing is that if you have a clearColor as the shadowLayer.backgroundColor is just didn't draw.
真正有趣的是,如果你有一个 clearColor 作为 shadowLayer.backgroundColor 只是没有绘制。