给我的 UIView 添加阴影的最好方法是什么

我试图添加一个下拉阴影的意见,是一个又一个分层,视图崩溃允许其他视图的内容可以看到,在这种情况下,我想保持 view.clipsToBounds开,以便当视图崩溃他们的内容被剪辑。

这似乎已经使我很难添加一个下降阴影的图层,因为当我打开 clipsToBounds的阴影也被剪辑。

我一直试图操纵 view.frameview.bounds,以便添加一个下降阴影的框架,但允许的界限足够大,以包围它,但我没有这个运气。

下面是我用来添加 Shadow 的代码(这只适用于如图所示的 clipsToBounds OFF)

view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;

这里是一个截图的阴影被应用到顶部最轻的灰色层。如果 clipsToBounds是 OFF 的话,希望这可以给出我的内容如何重叠的想法。

Shadow Application.

我怎样才能添加一个阴影到我的 UIView和保持我的内容剪辑?

编辑: 只是想补充一下,我也曾经尝试过使用带阴影的背景图片,效果不错,不过我还是想知道最好的编码解决方案。

146517 次浏览

Try this:

UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;

First of all: The UIBezierPath used as shadowPath is crucial. If you don't use it, you might not notice a difference at first, but the keen eye will observe a certain lag occurring during events like rotating the device and/or similar. It's an important performance tweak.

Regarding your issue specifically: The important line is view.layer.masksToBounds = NO. It disables the clipping of the view's layer's sublayers that extend further than the view's bounds.

For those wondering what the difference between masksToBounds (on the layer) and the view's own clipToBounds property is: There isn't really any. Toggling one will have an effect on the other. Just a different level of abstraction.


Swift 2.2:

override func layoutSubviews()
{
super.layoutSubviews()


let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowOffset = CGSizeMake(0.0, 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.CGPath
}

Swift 3:

override func layoutSubviews()
{
super.layoutSubviews()


let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.cgPath
}

The trick is defining the masksToBounds property of your view's layer properly:

view.layer.masksToBounds = NO;

and it should work.

(Source)

Wasabii's answer in Swift 2.3:

let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.blackColor().CGColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.CGPath

And in Swift 3/4/5:

let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.cgPath

Put this code in layoutSubviews() if you're using AutoLayout.

In SwiftUI, this is all much easier:

Color.yellow  // or whatever your view
.shadow(radius: 3)
.frame(width: 200, height: 100)

On viewWillLayoutSubviews:

override func viewWillLayoutSubviews() {
sampleView.layer.masksToBounds =  false
sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
sampleView.layer.shadowOpacity = 1.0
}

Using Extension of UIView:

extension UIView {


func addDropShadowToView(targetView:UIView? ){
targetView!.layer.masksToBounds =  false
targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
targetView!.layer.shadowOpacity = 1.0
}
}

Usage:

sampleView.addDropShadowToView(sampleView)

You can create an extension for UIView to access these values in the design editor

Shadow options in design editor

extension UIView{


@IBInspectable var shadowOffset: CGSize{
get{
return self.layer.shadowOffset
}
set{
self.layer.shadowOffset = newValue
}
}


@IBInspectable var shadowColor: UIColor{
get{
return UIColor(cgColor: self.layer.shadowColor!)
}
set{
self.layer.shadowColor = newValue.cgColor
}
}


@IBInspectable var shadowRadius: CGFloat{
get{
return self.layer.shadowRadius
}
set{
self.layer.shadowRadius = newValue
}
}


@IBInspectable var shadowOpacity: Float{
get{
return self.layer.shadowOpacity
}
set{
self.layer.shadowOpacity = newValue
}
}
}

You can set shadow to your view from storyboard also

enter image description here

So yes, you should prefer the shadowPath property for performance, but also: From the header file of CALayer.shadowPath

Specifying the path explicitly using this property will usually * improve rendering performance, as will sharing the same path * reference across multiple layers

A lesser known trick is sharing the same reference across multiple layers. Of course they have to use the same shape, but this is common with table/collection view cells.

I don't know why it gets faster if you share instances, i'm guessing it caches the rendering of the shadow and can reuse it for other instances in the view. I wonder if this is even faster with