I have a UIView
with the following properties:
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
This results in the following:
...while I do not want to see the shadow beneath the view like this:
How can I draw the shadow outside the view only so it is not visible below it?
Thanks in advance!
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)
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.
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
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.