为什么设置 masksToBounds = YES 会阻止 CALayer 的阴影效果?

86
使用以下代码片段,我将一个UIView添加了一个投影效果。它运行得相当不错。但是一旦我将视图的masksToBounds属性设置为YES,投影效果就不再呈现。
self.myView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.myView.layer.shadowOpacity = 1.0;
self.myView.layer.shadowRadius = 10.0;
self.myView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.myView.layer.cornerRadius = 5.0;
self.myView.layer.masksToBounds = YES; // <-- This is causing the Drop shadow to not be rendered
UIBezierPath *path = [UIBezierPath bezierPathWithCurvedShadowForRect:self.myView.bounds];
self.myView.layer.shadowPath = path.CGPath;
self.myView.layer.shouldRasterize = YES;

你有什么想法吗?

6个回答

171

由于阴影是在视图之外进行的效果,而将 masksToBounds 设置为 YES 将告诉 UIView 不要绘制超出自身范围的任何内容。

如果您想要一个带有阴影的圆角视图,我建议您使用 2 个视图来实现:

UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];

view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];

1
只是一个提示,请确保父视图[在这种情况下为view2]具有清晰的背景颜色,这就是我的问题所在。 - Adam Carter
@GangstaGraham请确保将view2作为view1的子视图添加。 - pxpgraphics
正是我所需要的! - Rohan Sanap

18

现在是iOS 6,事情可能已经改变。TheSquad的答案对我来说不起作用,直到我添加了另一行view2.layer.masksToBounds = NO;,否则阴影不会显示。虽然文档说masksToBounds默认为NO,但我的代码显示相反。

以下是我如何制作带阴影的圆角按钮,这是我应用程序中最常用的代码片段之一。

button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;

view.layer.masksToBounds = NO;      // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath = 
    [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;      

[view addSubview:button];

编辑

如果需要对视图进行动画或滚动,masksToBounds = YES会严重影响性能,这意味着动画可能会出现卡顿。要实现圆角和阴影以及平滑的动画或滚动,请改用以下代码:

button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;

view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;  

[view addSubview:button];

4
第一个答案加一分。你更新后的答案无法处理圆角。如果将“masksToBounds”设置为“NO”,则圆角将消失。而不是更改它,您可以使用“shouldRasterize”属性以获得更好的性能。 - Dinesh Raja
这个在2020年已经可以工作了。只需要添加一行简单的解决方案。 - GeneCode

5

使用Storyboard的Swift 3.0版本

与@TheSquad的想法一样。在实际视图下创建一个新视图,并向下添加阴影。

1. 在实际视图下创建一个视图

UIView拖到Storyboard中,使用与目标视图相同的约束条件。勾选目标视图的剪切边界。还要确保新视图在目标视图之前列出,以便目标视图将覆盖新视图。

enter image description here

2. 现在将新视图链接到您的代码并在其上添加阴影

这只是一个示例。您可以按任何方式进行操作。

shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3

shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true

4
这是由@TheSquad发布的Swift 3和IBDesignable版本的答案。
在故事板文件中做出更改时,我使用了相同的概念。首先,我将我的 targetView (需要圆角和阴影的视图)移动到一个新的 containerView 中。然后,我添加了以下代码行(参考:https://dev59.com/_ms05IYBdhLWcg3wDtln#35372901),为UIView类添加了一些IBDesignable属性:
@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
    }
}

/* The corner radius of the view. */
@IBInspectable var cornerRadius: CGFloat {
    set {
        layer.cornerRadius = newValue
    }
    get {
        return layer.cornerRadius
    }
}

在添加了这段代码后,我回到Storyboard,在选择我的containerView时,我现在可以在属性检查器中找到一组新的属性:

enter image description here

除了根据我的选择为这些属性添加值外,我还为我的targetView添加了一个圆角,并将masksToBounds属性设置为true。
希望这能帮到你 :)

2

我在使用阴影和圆角时遇到了严重的性能问题。但是,不需要使用shadowPath部分,只需使用以下几行代码就可以完美地解决性能问题:

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;

然而,使用此功能无法对屏幕进行动画或重新布局。 - Van Du Tran

1
这是其中一种解决方案:

     @IBOutlet private weak var blockView: UIView! {
         didSet {
          blockView.backgroundColor = UIColor.white
          blockView.layer.shadowColor = UIColor.black.cgColor
          blockView.layer.shadowOpacity = 0.5
          blockView.layer.shadowOffset = CGSize.zero

          blockView.layer.cornerRadius = 10
        }
      }
      @IBOutlet private weak var imageView: UIImageView! {
        didSet {
          imageView.layer.cornerRadius = 10
          imageView.layer.masksToBounds = true

          imageView.layer.shouldRasterize = true
        }
      }

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接