What's the best way to add a drop shadow to my UIView

☆樱花仙子☆ 提交于 2019-11-28 02:40:28
pkluz

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
}

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:

    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.

sergio

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

view.layer.masksToBounds = NO;

and it should work.

(Source)

You can create an extension for UIView to access these values in the 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

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)

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!