Draw Drop Shadow Outside UIView

不问归期 提交于 2019-12-06 02:44:10

问题


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:

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

Question

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

Thanks in advance!


回答1:


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:

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)



回答2:


I usually solve this kind of situation using composition of views. Instead of setting the shadow in the target view, I create a ShadowView and I put it behind the target view, with the same frame.

Doing so, we can mask the shadow view in order to only draw the shadow outside its frame.

The code for the shadow's mask is (full code above):

      let maskLayer = CAShapeLayer()
      let path = CGMutablePath()
      path.addPath(UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets.zero), cornerRadius: CGFloat(cornerRadius)).cgPath)
      path.addPath(UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets(top: -offset.height - radius*2, left: -offset.width - radius*2, bottom: -offset.height - radius*2, right: -offset.width - radius*2)), cornerRadius: CGFloat(cornerRadius)).cgPath)
      maskLayer.backgroundColor = UIColor.black.cgColor
      maskLayer.path = path;
      maskLayer.fillRule = .evenOdd
      self.layer.mask = maskLayer

The result is the following:

Full playground available here: https://gist.github.com/llinardos/d1ede3b491efc8edac940c5ea8631c6f

This kind of effect looks really nice with blurred backgrounds.




回答3:


This was adapted a bit from a post found here. I did not know the specifics of your layer so I created a circle centered in the middle of the screen instead. You could easily replace the circle with the specifications of your layer and I believe that will correct your issues. Feel free to comment for any further instruction! import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    createOverlay(view.bounds)
}
func createOverlay(frame : CGRect)
{
    let overlayView = UIView(frame: frame)
    overlayView.alpha = 0.6
    overlayView.backgroundColor = UIColor.blackColor()
    self.view.addSubview(overlayView)

    let maskLayer = CAShapeLayer()

    let path = CGPathCreateMutable()

    let radius : CGFloat = 50.0
    let xOffset : CGFloat = view.bounds.width / 2
    let yOffset : CGFloat = view.bounds.height / 2

    CGPathAddArc(path, nil, overlayView.frame.width - xOffset, yOffset, radius, 0.0, 2 * 3.14, false)
    CGPathAddRect(path, nil, CGRectMake(0, 0, overlayView.frame.width, overlayView.frame.height))
    maskLayer.backgroundColor = UIColor.blackColor().CGColor
    maskLayer.path = path;
    maskLayer.fillRule = kCAFillRuleEvenOdd

    overlayView.layer.mask = maskLayer
    overlayView.clipsToBounds = true
}
}

Photo



来源:https://stackoverflow.com/questions/38330582/draw-drop-shadow-outside-uiview

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