#ios #uibutton #border #gradient #shadow
#iOS #uibutton #граница #градиент #тень
Вопрос:
На сайте есть несколько похожих вопросов, но я ищу что-то конкретное и немного другое.
Я следовал указаниям, приведенным здесь: http://www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers создать подкласс UIButton, чтобы создать универсальный класс, в котором я мог бы указывать цвета градиента, вместо того, чтобы пытаться использовать статическое изображение.
Я столкнулся с проблемой, когда setMasksToBounds на слое кнопки либо позволял А) отображать отбрасываемую тень, но также позволял градиентному слою отображаться за пределами закругленных углов, либо Б) градиентный слой обрезался до закругленных углов, но не позволял отображать отбрасываемую тень
Мое решение проблемы показалось неуклюжим и (хотя оно работает) Я хотел посмотреть, знает ли кто-нибудь лучший и / или более чистый способ выполнить то же самое. Вот мой код:
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 amp;amp; _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
Как вы можете видеть, я добавляю слой-оболочку, на который может безопасно маскироваться градиентный слой, в то время как верхний уровень отображения кнопки может безопасно устанавливать masksToBounds = NO при добавлении отбрасываемой тени. Я добавляю setCornerRadius: метод, позволяющий настраивать как верхний слой, так и «оболочку».
Поэтому вместо того, чтобы делать что-то вроде, [[myCustomButton layer] setCornerRadius:3.0f];
я просто говорю [myCustomButton setCornerRadius:3.0f];
, что, как вы можете видеть, это, возможно, не так чисто, как я надеюсь.
Есть ли способ лучше?
Комментарии:
1. по иронии судьбы этот вопрос может оказаться менее актуальным с iOS 7 🙂
Ответ №1:
Таким образом я обнаружил, что у кнопки есть закругленный угол, градиент и отбрасываемая тень. В этом примере используется один конкретный градиент, но, очевидно, его можно заменить другими градиентами.
@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
Комментарии:
1. Привет, Ischult2 — можете ли вы включить файл заголовка или указать, каков суперкласс этой реализации?
2. это подкласс UIButton (судя по коду и основному вопросу). Я использовал этот код, и он работает идеально. Внес ряд изменений, чтобы кнопки выглядели лучше. Это должно быть отмечено как ответ
3. Помогли ваши расположения градиентов. Есть ли причина, по которой вы так выполняете математику? не могли бы вы просто сделать что-то вроде… 1 * 0,9 или 1 * 0,95 или даже просто 0,9 или 0,95 явно? Возможно, я что-то упускаю, если бы вы могли объяснить, почему ваша математика такая для расположения градиентов. приветствия
4. Это отлично сработало, но градиент появляется над текстом кнопки. Изменение на вставку слоя с индексом: 0 вместо этого делает свое дело:
[self.layer insertSublayer:gradient atIndex:0];
5. Все, о чем я не знал, это: —
gradient.cornerRadius = 10;
Ответ №2:
Вместо вставки градиентного слоя вы также можете переопределить метод, layerClass
чтобы вернуть CAGradientLayer
класс. Слой кнопки не относится к этому классу, и вы можете легко настроить его цвета и т.д.
(Class)layerClass {
return [CAGradientLayer class];
}
Ответ №3:
У меня была похожая проблема, однако вместо градиента у меня было изображение для фона. В конце концов я решил это с помощью:
(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, он просто не прорисовывается.