首页 文章

如何在UIView下绘制阴影?

提问于
浏览
338

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

  • 保存图形状态 .

  • 调用函数 CGContextSetShadow ,传递适当的值 .

  • 执行要应用阴影的所有图形 .

  • 恢复图形状态

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

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

..但是这对于(a)接下来要去哪里以及(b)如果有什么我需要对我的 UIView 进行这项工作有点困难?

14 回答

  • 39

    在当前代码中,保存当前上下文的 GState ,将其配置为绘制阴影..并将其恢复为将其配置为绘制阴影之前的状态 . 然后,最后,您调用超类的 drawRect 实现: .

    任何应该受阴影设置影响的绘图都需要在之后发生

    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    

    但之前

    CGContextRestoreGState(currentContext);
    

    所以,如果你希望超类的 drawRect: 在阴影中是'wrapped',那么如果重新安排你的代码怎么样呢?

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

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

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

    您需要导入QuartzCore .

    #import <QuartzCore/QuartzCore.h>
    
  • 13
    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;
    
  • 152

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

    例如:

    enter image description here

  • 3

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

  • 93

    使用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中为Utilities面板> Attributes Inspector中的每个视图提供:

    Utilities Panel

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

    笔记:

    • 阴影不会出现在IB中,仅在运行时出现 .
    • 正如Mazen Kasser所说

    对于未能使其工作的人[...]确保未启用剪辑子视图(clipsToBounds)

  • 778

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

  • 1

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

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

  • 0

    如果您想使用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

  • 0

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

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

    希望它能帮到你!!!

  • 7

    所有答案都很好,但我想补充一点

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

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

    对于Xamarians同事,答案的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 的实例,您可以在其上直接调用相应的方法 .

相关问题