UIButton w/ gradient, rounded corners, border, and drop shadow

На сайте есть несколько похожих вопросов, но я ищу что-то конкретное и немного другое.

Я следовал направлению, указанному здесь: http://www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers/ для подкласса UIButton, чтобы создать общий класс, которому я могу задать цвета градиента, а не пытаться использовать статическое изображение.

Я столкнулся с проблемой, когда setMasksToBounds на слое кнопки либо позволяет A) показать падающую тень, но при этом позволить градиентному слою выйти за пределы закругленных углов ИЛИ B) градиентный слой прижимается к закругленным углам, но не позволяет отображаться падающей тени

Мое решение проблемы показалось мне громоздким, и (хотя оно работает) я хотел узнать, знает ли кто-нибудь лучший и/или более чистый способ добиться того же самого. Вот мой код:

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 (интересные части)

#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

Как вы можете видеть, я добавляю "оберточный" слой, к которому градиентный слой может безопасно маскироваться, а CALayer верхнего уровня представления кнопки может безопасно установить masksToBounds = NO, когда добавляется падающая тень. Я добавляю метод setCornerRadius:, чтобы и верхний слой, и "обертка" могли регулировать.

Поэтому вместо того, чтобы делать что-то вроде [[myCustomButton layer] setCornerRadius:3.0f]; я просто говорю [myCustomButton setCornerRadius:3.0f]; Как вы можете видеть, это не так чисто, как я надеялся.

Есть ли лучший способ?

16
задан jinglesthula 11 May 2011 в 21:54
поделиться