如何在UIView下绘制阴影?


352

我正在尝试UIView在Cocoa Touch 的底部边缘下方绘制阴影。我知道我应该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进行这项工作感到有些困惑?

Answers:


98

在当前代码中,保存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);
}

789

到目前为止,一种更简单的方法是在初始化时设置视图的某些图层属性:

self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

您需要导入QuartzCore。

#import <QuartzCore/QuartzCore.h>

4
但是请注意,这仅适用于iOS 3.2+,因此,如果您的应用程序应在旧版本上运行,则必须使用Christian的解决方案或视图后的静态图像(如果可以)。
florianbuerger 2011年

119
此解决方案还需要添加#import <QuartzCore/QuartzCore.h>"到.h文件。
MusiGenesis 2011年

26
设置masksToBoundsNO将否定cornerRadius,不是吗?
pixelfreak 2012年

4
好的,要解决这个问题,需要在图层上设置backgroundColor,并且视图必须透明。
pixelfreak 2012年

@pixelfreak你怎么做?我尝试过self.layer.backgroundColor = [[UIColor whiteColor] CGColor];但没有运气。哪个视图需要透明?
Victor Van Hee

232
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;

1
可能值得注意的是,这种优化仅在视图为矩形时才有用。
本杰明·多贝尔

1
self.layer.shadowPath ...而不是什么?或只是添加
-Chrizzz

2
只需添加其他行即可。
christophercotton

11
@NathanGaskin-绘制阴影是昂贵的操作,因此,例如,如果您的应用程序允许其他界面方向并且您开始旋转设备,而没有明确指定阴影的路径,则必须在该动画期间多次渲染阴影,具体取决于形状,可能明显减慢了动画的速度
Peter Pajchl 2011年

9
@BenjaminDobell该特定行仅适用于矩形,但您也可以创建非矩形路径。例如,如果您有一个圆角矩形,则可以使用bezierPathWithRoundedRect:cornerRadius:
Dan Dyer

160

相同的解决方案,只是提醒您:您可以直接在情节提要中定义阴影。

例如:

在此处输入图片说明


2
可悲的是,我认为CGColor不在情节
提要中

1
只需为属性定义类别UIView或将CGLayer其定义UIColorCGColor属性;)
DeFrenZ 2014年

1
该链接描述了操作方法:cocoadventures.org/post/104137872754/…–
堪察加

8
为了使人们更容易复制和粘贴它:layer.masksToBound,layer.shadowOffset,layer.shadowRadius,layer.shadowOpacity。错别字会在这里杀死你。
etayluz

3
layer.shadowOpacity值应为0.5而不是0.5
Desert Rose

43

您可以尝试这个....您可以使用这些值。该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

传播实例

传播实例

创建基本阴影

    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示例

输出值


19

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

然后,它将在“界面构建器”中为“实用工具面板”>“属性检查器”中的每个视图提供:

实用程序面板

您现在可以轻松设置阴影。

注意:
-阴影仅在运行时不会出现在IB中。
-正如Mazen Kasser所说

对于未能通过此操作的人,请确保clipsToBounds未启用“ 剪辑子视图”()


这是正确的答案-为将来的接口配置代码
Crake

此解决方案导致我无法正常工作,并显示以下警告消息(每个属性一个):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.
Xvolks

1
我必须进行导入UIKit才能使其正常工作,Foundation由XCode创建的基本导入还不够,但是编译良好。我应该复制了整个源代码。谢谢Axel提供的出色解决方案。
Xvolks '16

13

我将此作为我的工具的一部分。这样我们不仅可以设置阴影,而且可以为任何一个圆角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];


7

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

如果您要使用这种方法,我会考虑添加参数,以便您可以调整值以使其动态变化
Josh O'Connor

这不是在给我圆圆的角,我做错了什么吗?
Nikhil Manapure

@NikhilManapure没有任何反应,这可能是因为installShadow()未从viewDidLoad()viewWillAppear()
Neoneye

我从重载draw的角度来称呼它。阴影正确到来,但圆角不正确。
Nikhil Manapure

@NikhilManapure没有圆形的边框,这可能是因为视图是UIStackView仅布局而没有视图的视图。您可能必须插入一个常规UIView容器作为所有容器。
neoneye

6

如果您想使用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.您现在可以在情节提要中使用这些属性故事板图像


3

对于在这里尝试了所有答案后都无法正常工作的人(以我自己!),只需确保未在“属性”检查器中启用“ 剪辑子视图” ...


1

迅捷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

1

您可以使用为阴影和拐角半径创建的我的实用程序函数,如下所示:

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

希望对您有帮助!!!


1

全部回答都很好,但我想再加一点

如果在拥有表格单元格时遇到问题,请为一个新单元格出队,则阴影会不匹配,因此,在这种情况下,您需要将阴影代码放置在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;
}

1

对于Xamarian同伴,答案的Xamarin.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直接调用适当方法的实例。


1

您可以使用它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)

0

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

如何使用它

演示

逐个草图和XCODE

影子实例

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

输出值

演示输出


您可以改进从属性中删除阴影前缀的代码。ShadowView的目标明确于阴影操作。还有一件事是,您可以向此类添加角半径。(在今天,大多数阴影视图涉及角半径)
mathema
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.