UIButton w / gradient, esquinas networkingondeadas, borde y sombra

Hay algunas preguntas similares en el sitio, pero estoy buscando algo específico y ligeramente diferente.

Seguí la dirección dada aquí: http://www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers/ a la subclass UIButton para crear una class genérica que pueda especifique los colors del gradiente, en lugar de tratar de usar una image estática.

Me encontré con un problema donde el setMasksToBounds en la capa del button permitiría A) mostrar la sombra paralela, pero también permitir que la capa de degradado se muestre más allá de las esquinas networkingondeadas O B) la capa de degradado para sujetar a las esquinas networkingondeadas, pero no permitir que se muestre la sombra

Mi solución al problema parecía torpe y (aunque funciona) quería ver si alguien sabía de una manera mejor y / o más limpia para lograr lo mismo. Aquí está mi código:

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 (las partes interesantes, de todos modos)

 #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 obscunetworking [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 

Como puede ver, agrego una capa de "contenedor" a la que la capa de degradado puede enmascarar de manera segura, mientras que la vista de button CALayer de nivel superior puede establecer con security máscaras de Contenedores = NO cuando se agrega la sombra de caída. Agrego un método setCornerRadius: para permitir que tanto la capa superior como la 'envoltura' se ajusten.

Entonces, en lugar de hacer algo como [[myCustomButton layer] setCornerRadius:3.0f]; Acabo de decir [myCustomButton setCornerRadius:3.0f]; Como puede ver, no es tan limpio como espero.

¿Hay una mejor manera?

Esta es la manera que encontré para tener un button con esquina networkingondeada, gradiente y sombra. Este ejemplo tiene un gradiente particular, pero obviamente puede replacese por otros gradientes.

 @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 

En lugar de insert una capa de degradado, también puede anular el método +layerClass para devolver la class CAGradientLayer . La capa del button es de esa class, y puedes configurar fácilmente sus colors, etc.

 + (Class)layerClass { return [CAGradientLayer class]; } 

Tuve un problema similar, sin embargo, en lugar de un degradado, tenía una image para un background. Lo solucioné al final usando:

 + (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]; } 

Lo realmente interesante es que si tienes un color claro como shadowLayer.backgroundColor es que no dibujaste.