如何在UIView下绘制阴影?

时间:2009-04-30 08:07:04

标签: iphone objective-c ios cocoa-touch core-graphics

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

  1. 保存图形状态。
  2. 调用函数CGContextSetShadow,传递适当的值。
  3. 执行要应用阴影的所有绘图。
  4. 恢复图形状态
  5. 我在UIView子类中尝试了以下内容:

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

    ..但这对我不起作用,我有点不知道(a)下一步该去哪里和(b)如果我需要对UIView做任何事情来完成这项工作?

16 个答案:

答案 0 :(得分:784)

更简单的方法是在初始化时设置视图的某些图层属性:

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

您需要导入QuartzCore。

#import <QuartzCore/QuartzCore.h>

答案 1 :(得分:230)

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;

答案 2 :(得分:156)

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

例如:

enter image description here

答案 3 :(得分:93)

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

答案 4 :(得分:41)

你可以尝试这个......你可以玩这些值。 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
  

传播示例

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

OUTPUT

答案 5 :(得分:19)

Simple and clean solution using Interface Builder

Add a file named UIView.swift in your project (or just paste this in any file) :

cell

Then this will be available in Interface Builder for every view in the Utilities Panel > Attributes Inspector :

Utilities Panel

You can easily set the shadow now.

Notes:
- The shadow won't appear in IB, only at runtime.
- As Mazen Kasser said

To those who failed in getting this to work [...] make sure Clip Subviews (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 } } } ) is not enabled

答案 6 :(得分: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 :(得分:7)

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

答案 8 :(得分:4)

如果您想使用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步。您现在可以在情节提要中使用这些属性storyboard image

答案 9 :(得分:3)

对于那些在尝试所有答案后未能使其工作的人(正如我自己!),只需确保在属性检查器中未启用剪辑子视图 ...

答案 10 :(得分:1)

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

eureka.instance.instance-id==${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${random.value}}

希望它能帮到你!!!

答案 11 :(得分:1)

所有答案都很好,但我想再增加一点

如果在有表格单元格时遇到问题,则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;
}

答案 12 :(得分: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的实例,可以直接在其上调用适当的方法。

答案 13 :(得分:0)

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

答案 14 :(得分:0)

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

答案 15 :(得分:0)

在Swift 4中使用IBDesignable和IBInspectable素描阴影

  

如何使用它

DEMO

  

逐边素描和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