Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw Drop Shadow Outside UIView

Background

I have a UIView with the following properties:

  • alpha = 1
  • backgroundColor = white, with 0.35 opacity
  • rounded corners
  • drop shadow

Code

This is how I create the drop shadow: (UIView extension)

self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.darkGrayColor().CGColor
self.layer.shadowOffset = CGSizeMake(0, 5)
self.layer.shadowOpacity = 0.35
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).CGPath

Results

This results in the following:

current result

...while I do not want to see the shadow beneath the view like this:

wished result

Question

How can I draw the shadow outside the view only so it is not visible below it?

Thanks in advance!

like image 415
LinusGeffarth Avatar asked Jul 12 '16 13:07

LinusGeffarth


People also ask

What is shadowOffset?

ShadowOffset is a CGSize representing how far to offset the shadow from the path. The default value of this property is (0.0, -3.0).

What is shadow opacity in Swift?

shadowOpacity sets how transparent the shadow is, where 0 is invisible and 1 is as strong as possible. shadowOffset sets how far away from the view the shadow should be, to give a 3D offset effect.


1 Answers

This is perhaps slightly more complex than it would need to be, but here's one solution.

Extend UIView with the following method:

extension UIView {
    // Note: the method needs the view from which the context is taken as an argument.
    func dropShadow(superview: UIView) {
        // Get context from superview
        UIGraphicsBeginImageContext(self.bounds.size)
        superview.drawViewHierarchyInRect(CGRect(x: -self.frame.minX, y: -self.frame.minY, width: superview.bounds.width, height: superview.bounds.height), afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        // Add a UIImageView with the image from the context as a subview
        let imageView = UIImageView(frame: self.bounds)
        imageView.image = image
        imageView.layer.cornerRadius = self.layer.cornerRadius
        imageView.clipsToBounds = true
        self.addSubview(imageView)

        // Bring the background color to the front, alternatively set it as UIColor(white: 1, alpha: 0.2)
        let brighter = UIView(frame: self.bounds)
        brighter.backgroundColor = self.backgroundColor ?? UIColor(white: 1, alpha: 0.2)
        brighter.layer.cornerRadius = self.layer.cornerRadius
        brighter.clipsToBounds = true
        self.addSubview(brighter)

        // Set the shadow
        self.layer.masksToBounds = false
        self.layer.shadowColor = UIColor.darkGrayColor().CGColor
        self.layer.shadowOffset = CGSizeMake(0, 5)
        self.layer.shadowOpacity = 0.35
        self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).CGPath
    }
}

Usage, considering the background view is named view:

let shadowView = UIView(frame: CGRect(x: 100, y: 100, width: 300, height: 200))
shadowView.layer.cornerRadius = 15.0
shadowView.dropShadow(view)

view.addSubview(shadowView)

Which results in a view like this:

UIView

Note: the dropShadow method can not be called from viewDidLoad as this causes issues with the graphics context. So, use this method in viewWillAppear earliest for the above result.


Here's the code for the background view, just in case somebody wants to test in playgrounds:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 400))
view.backgroundColor = UIColor.clearColor()

let color1 = UIColor(hue: 0.39, saturation: 0.7, brightness: 1.0, alpha: 1.0).CGColor
let color2 = UIColor(hue: 0.51, saturation: 0.9, brightness: 0.6, alpha: 1.0).CGColor

let gradient = CAGradientLayer()
gradient.frame = view.frame
gradient.colors = [color1, color2]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
view.layer.insertSublayer(gradient, atIndex: 0)
like image 102
xoudini Avatar answered Nov 13 '22 20:11

xoudini