为什么掩码 ToBound = YES 可以防止 CALayer 阴影?

通过以下代码片段,我将为我的 UIView 添加一个阴影效果。效果不错。但是只要我将视图的 面具禁止入内属性设置为 是的。阴影效果不再呈现。

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;

你有什么想法吗?

57693 次浏览

Because shadow is an effect done outside the View, and that masksToBounds set to YES will tell the UIView not to draw anything that is outside itself.

If you want a roundedCorner view with shadow I suggest you do it with 2 views:

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

It's iOS 6 now, things might have changed. TheSquad's answer don't work for me until I managed to add one more line view2.layer.masksToBounds = NO;, otherwise shadow doesn't show. Although documentation says masksToBounds is NO by default, my code shows the opposite.

Here is how I make a rounded corner button with shadow, which is among the most commonly used code snippet in my app.

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

EDIT

If views need to be animated or scrolled, masksToBounds = YES tax performance significantly, which means animation will probably get stuttered. To get rounded corner and shadow AND smooth animation or scrolling, use following code instead:

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

I also had drastic performance issues with shadows and rounded corners. Instead of using the shadowPath part, I used the following lines which perfectly solved the performance hit:

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

This is the Swift 3 and IBDesignable version of the answer posted by @TheSquad.

I used the same concept while making changes in the storyboard file. First I moved my targetView (the one which requires corner radius and shadow) inside a new containerView. Then I added the following lines of code (Reference: https://stackoverflow.com/a/35372901/419192) to add some IBDesignable attributes for UIView Class:

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

After adding this code, I went back to the storyboard and on selecting my containerView I could now find a new set of attributes in the attributes inspector:

enter image description here

Other than adding values for these attributes as per my choice, I also added a corner radius to my targetView and set the masksToBounds property as true.

I hope this helps :)

Swift 3.0 version with StoryBoard

The same idea with @TheSquad. Create a new view under the actual view and add shadow to the lower view.

1. Create a view under the actual view

Drag a UIView to StoryBoard with same constraint as your target view. Check clip to bound for the target view. Also make sure the new view is listed before the target view so that the target view will cover the new view.

enter image description here

2. Now link the new view to your code add add shadow on it

This is just a sample. You can do whatever way you want here

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

Here is one of the solutions:

     @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