Кнопка UIB с градиентом, закругленными углами, границей и тенью - PullRequest
16 голосов
/ 12 мая 2011

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

Я следовал приведенному здесь направлению: 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]; Как вы можете видеть, это не так чисто, как я надеюсь.

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

Ответы [ 3 ]

22 голосов
/ 21 апреля 2012

Так я нашел кнопку с закругленным углом, градиентом и тенью. Этот пример имеет один конкретный градиент, но, очевидно, его можно заменить другими градиентами.

@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
9 голосов
/ 30 июня 2011

Вместо вставки градиентного слоя вы также можете переопределить метод +layerClass, чтобы вернуть класс CAGradientLayer.Слой кнопки соответствует классу, и вы можете легко установить его цвета и т. Д.

+ (Class)layerClass {
    return [CAGradientLayer class];
}
4 голосов
/ 30 мая 2013

У меня была похожая проблема, однако вместо градиента у меня было изображение для фона. Я решил это в конце, используя:

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

Действительно интересная вещь заключается в том, что если у вас есть clearColor, поскольку shadowLayer.backgroundColor просто не рисует.

...