Я пытаюсь нарисовать тень под нижним краем UIView
в Cocoa Touch. Я понимаю, что я должен использовать CGContextSetShadow()
для рисования тени, но руководство по программированию 2D кварца немного расплывчато:
CGContextSetShadow
, передав соответствующие значения.Я пробовал следующее в подклассе UIView
:
- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
CGContextRestoreGState(currentContext);
[super drawRect: rect];
}
.. но это не работает для меня, и я немного застрял (а), куда идти дальше и (б) если мне что-то нужно сделать для моего UIView
, чтобы сделать эту работу?
В вашем текущем коде вы сохраняете GState
текущего контекста, настраиваете его на рисование тени.. и восстанавливаете его до того, что было до того, как вы настроили его, чтобы нарисовать тень. Затем, наконец, вы вызываете реализацию суперкласса drawRect
:.
Любой рисунок, на который должен влиять параметр тени, должен произойти после
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
но до
CGContextRestoreGState(currentContext);
Итак, если вы хотите, чтобы суперкласс drawRect:
был "завернут" в тень, то как насчет того, измените ли вы свой код следующим образом?
- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
[super drawRect: rect];
CGContextRestoreGState(currentContext);
}
А гораздо проще использовать несколько атрибутов уровня представления при инициализации:
self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;
Вам нужно импортировать QuartzCore.
#import <QuartzCore/QuartzCore.h>
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 8; // if you like rounded corners
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;
Это замедлит работу приложения. Добавление следующей строки может повысить производительность, если ваше представление явно прямоугольное:
self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
То же решение, но только для того, чтобы напомнить вам: вы можете определить тень прямо в раскадровке.
Пример:
Вы можете попробовать это... вы можете играть со значениями.
shadowRadius
определяет количество размытия. shadowOffset
определяет, куда идет тень.
Swift 2.0
let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height
demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4) //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds = false
demoView.layer.shadowPath = shadowPath.CGPath
Swift 3.0
let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height
demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.black.cgColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4) //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds = false
demoView.layer.shadowPath = shadowPath.cgPath
Пример с расширением
Чтобы создать базовую тень
demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSizeMake(0.5, 4.0); //Here your control your spread
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
Пример базовой тени в Swift 2.0
Простое и чистое решение с помощью Interface Builder
Добавьте в проект файл UIView.swift(или просто вставьте его в любой файл):
import UIKit
@IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
* from patterns are currently NOT supported. Animatable. */
@IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.CGColor
}
get {
if let color = layer.shadowColor {
return UIColor(CGColor:color)
}
else {
return nil
}
}
}
/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
* [0,1] range will give undefined results. Animatable. */
@IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
/* The shadow offset. Defaults to (0, -3). Animatable. */
@IBInspectable var shadowOffset: CGPoint {
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
}
/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
@IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
}
Затем это будет доступно в Interface Builder для каждого вида в панели "Утилиты" > "Attributes Inspector":
Теперь вы можете легко установить тень.
Примечания:
- Тень не появится в IB, только во время выполнения.
- Как сказал Мазен Кассер
Для тех, кому не удалось заставить это работать [...], убедитесь, что Clip Subviews (
clipsToBounds
) не включен
Failed to set (shadowColor) user defined inspected property on (UICollectionViewCell): [<UICollectionViewCell> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key shadowColor.
Я использую это как часть моих утилов. При этом мы можем не только установить тень, но и получить закругленный угол для любого UIView
. Также вы можете установить, какую цветовую тень вы предпочитаете. Обычно черный предпочтительнее, но иногда, когда фон не белый, вам может понадобиться что-то другое. Вот что я использую -
in utils.m
+ (void)roundedLayer:(CALayer *)viewLayer
radius:(float)r
shadow:(BOOL)s
{
[viewLayer setMasksToBounds:YES];
[viewLayer setCornerRadius:r];
[viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
[viewLayer setBorderWidth:1.0f];
if(s)
{
[viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
[viewLayer setShadowOffset:CGSizeMake(0, 0)];
[viewLayer setShadowOpacity:1];
[viewLayer setShadowRadius:2.0];
}
return;
}
Чтобы использовать это, нам нужно назвать это - [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];
extension UIView {
func installShadow() {
layer.cornerRadius = 2
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1)
layer.shadowOpacity = 0.45
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shadowRadius = 1.0
}
}
Для тех, кому не удалось заставить это работать (как я!) после того, как вы попробовали все ответы здесь, просто убедитесь, что Подвижность Clip > не включена в инспекторе атрибутов...
Все Ответьте всем хорошо, но я хочу добавить еще одну точку
Если вы столкнулись с проблемой, когда у вас есть ячейки таблицы, Deque новая ячейка имеет несоответствие в тени, поэтому в этом случае вам нужно разместить теневой код в методе layoutSubviews, чтобы он хорошо себя вести во всех условиях.
-(void)layoutSubviews{
[super layoutSubviews];
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
[VPShadow applyShadowView:self];
}
или в ViewControllers для определенного вида разместите теневой код внутри следующего метода, чтобы он работал хорошо
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self.viewShadow layoutIfNeeded];
[VPShadow applyShadowView:self.viewShadow];
}
Я изменил свою теневую реализацию для новых разработчиков для более обобщенной формы ex:
/*!
@brief Add shadow to a view.
@param layer CALayer of the view.
*/
+(void)applyShadowOnView:(CALayer *)layer OffsetX:(CGFloat)x OffsetY:(CGFloat)y blur:(CGFloat)radius opacity:(CGFloat)alpha RoundingCorners:(CGFloat)cornerRadius{
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:cornerRadius];
layer.masksToBounds = NO;
layer.shadowColor = [UIColor blackColor].CGColor;
layer.shadowOffset = CGSizeMake(x,y);// shadow x and y
layer.shadowOpacity = alpha;
layer.shadowRadius = radius;// blur effect
layer.shadowPath = shadowPath.CGPath;
}
Вы можете использовать мою функцию утилиты, созданную для радиуса тени и угла, как показано ниже:
- (void)addShadowWithRadius:(CGFloat)shadowRadius withShadowOpacity:(CGFloat)shadowOpacity withShadowOffset:(CGSize)shadowOffset withShadowColor:(UIColor *)shadowColor withCornerRadius:(CGFloat)cornerRadius withBorderColor:(UIColor *)borderColor withBorderWidth:(CGFloat)borderWidth forView:(UIView *)view{
// drop shadow
[view.layer setShadowRadius:shadowRadius];
[view.layer setShadowOpacity:shadowOpacity];
[view.layer setShadowOffset:shadowOffset];
[view.layer setShadowColor:shadowColor.CGColor];
// border radius
[view.layer setCornerRadius:cornerRadius];
// border
[view.layer setBorderColor:borderColor.CGColor];
[view.layer setBorderWidth:borderWidth];
}
Надеюсь, это поможет вам.
Swift 3
self.paddingView.layer.masksToBounds = false
self.paddingView.layer.shadowOffset = CGSize(width: -15, height: 10)
self.paddingView.layer.shadowRadius = 5
self.paddingView.layer.shadowOpacity = 0.5
#import <QuartzCore/QuartzCore.h>"
в файл .h.