我如何在UIView下画一个阴影?

我试图在Cocoa Touch中的UIView的底部边缘下绘制一个阴影。我知道我应该使用CGContextSetShadow()来绘制阴影,但Quartz 2D编程指南有点模糊:

  1. 保存图形状态。
  2. 调用函数CGContextSetShadow,传递适当的值。
  3. 执行要应用阴影的所有绘图。
  4. 恢复图形状态

我已经在UIView子类中尝试了以下内容:

- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
CGContextRestoreGState(currentContext);
[super drawRect: rect];
}

..但这对我来说不工作,我有点卡住了(a)下一步要去哪里,(b)如果我需要对UIView做什么才能使这个工作?

256369 次浏览

在当前代码中,您保存当前上下文的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;

我把它作为我效用的一部分。有了这个,我们不仅可以设置阴影,还可以为任何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];

同样的解决方案,但只是提醒你:你可以直接在故事板中定义阴影。

例:

enter image description here

对于那些失败的人,在尝试了所有的答案后(就像我自己一样!),只要确保剪辑子视图没有在属性检查器中启用……

简单和干净的解决方案使用接口生成器

在你的项目中添加一个名为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
}
}
}

然后,这将在接口生成器中为实用工具面板>属性检查器中的每个视图可用:

Utilities Panel

现在你可以很容易地设置阴影。

< p >指出:< br > -阴影不会在IB中出现,只在运行时出现 -正如Mazen Kasser所说

对于那些未能实现这一目标的人……确保Clip Subviews (clipsToBounds)未启用

你可以试试....你可以调整数值。 shadowRadius指定了模糊的数量。

. shadowOffset指定阴影的位置

斯威夫特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

斯威夫特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

关于传播的例子

Example with spread .

创建一个基本的阴影

    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中的基本Shadow示例

OUTPUT

斯威夫特3

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
}
}

斯威夫特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

你可以使用我为阴影和角半径创建的效用函数,如下所示:

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

希望对你有所帮助!!

答得很好,但我想再补充一点

如果你遇到一个问题,当你有表格单元格,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];
}

我已经修改了我的影子实现为新的开发更普遍的形式,例如:

/*!
@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;
}

如果你想使用StoryBoard并且不想一直输入运行时属性,你可以很容易地为视图创建一个扩展,并使它们在StoryBoard中可用。

步骤1。创建扩展

extension UIView {


@IBInspectable var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}


@IBInspectable var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}


@IBInspectable var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}


@IBInspectable var maskToBound: Bool {
get {
return layer.masksToBounds
}
set {
layer.masksToBounds = newValue
}
}
}

步骤2。你现在可以在storyboardstoryboard image中使用这些属性

对萨玛里人来说,萨玛里人。iOS/ c#版本的答案如下所示:

public override void DrawRect(CGRect area, UIViewPrintFormatter formatter)
{
CGContext currentContext = UIGraphics.GetCurrentContext();
currentContext.SaveState();
currentContext.SetShadow(new CGSize(-15, 20), 5);
base.DrawRect(area, formatter);
currentContext.RestoreState();
}

主要的区别是你获得了CGContext的一个实例,你可以直接在这个实例上调用适当的方法。

你可以使用Extension来添加阴影

extension UIView {


func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float)
{
layer.masksToBounds = false
layer.shadowOffset = offset
layer.shadowColor = color.cgColor
layer.shadowRadius = radius
layer.shadowOpacity = opacity


let backgroundCGColor = backgroundColor?.cgColor
backgroundColor = nil
layer.backgroundColor =  backgroundCGColor
}
}

你可以称之为

your_Custom_View.addShadow(offset: CGSize(width: 0, height: 1), color: UIColor.black, radius: 2.0, opacity: 1.0)

在Swift 4中使用IBDesignable和IBInspectable绘制阴影

如何使用

DEMO

Sketch和xcode并排

Shadow Exampl

代码

@IBDesignable class ShadowView: UIView {


@IBInspectable var shadowColor: UIColor? {
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.shadowColor = color.cgColor
} else {
layer.shadowColor = nil
}
}
}


@IBInspectable var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}


@IBInspectable var shadowOffset: CGPoint {
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}


}


@IBInspectable var shadowBlur: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue / 2.0
}
}


@IBInspectable var shadowSpread: CGFloat = 0 {
didSet {
if shadowSpread == 0 {
layer.shadowPath = nil
} else {
let dx = -shadowSpread
let rect = bounds.insetBy(dx: dx, dy: dx)
layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
}
}
}

输出

DEMO OUTPUT