Add shadow around a masked UIView - ios

I am trying to create a pop-over which will have a tip. Following is the code for this
tipView.frame = CGRect(x: at.x - size.width, y: at.y, width: size.width, height: size.height)
let imgView = UIImageView.init(frame: CGRect(x: 0, y: 0, width: tipView.frame.width, height: tipView.frame.height))
imgView.image = #imageLiteral(resourceName: "popup.png")
tipView.mask = imgView
I masked UIView with an image which is in the shape of pop-over.
Now I want to add shadow to the UIView on all 4 sides. I tried all the methods. But the shadow is not visible

Add following code to add shadow and corner radius to tipView. Clear backgroundColor and make clipsToBounds = false of tipView.
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(roundedRect: tipView.bounds, byRoundingCorners: [.topLeft, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
shapeLayer.fillColor = UIColor.darkGray.cgColor
shapeLayer.masksToBounds = false
shapeLayer.shadowColor = UIColor.darkGray.cgColor
shapeLayer.shadowPath = shapeLayer.path
shapeLayer.shadowOffset = CGSize(width: 0, height: 2)
shapeLayer.shadowOpacity = 0.5
shapeLayer.shadowRadius = 2.0
tipView.layer.insertSublayer(shapeLayer, at: 0)

Add this code to your view layer:
// set the corner radius
layer.cornerRadius = 6.0
layer.masksToBounds = false
// set the shadow properties
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowOpacity = 0.2
layer.shadowRadius = 4.0
Please go through the this link for details

Related

Rounded corners and shadow for navigation bar

I want to create something like this:
Output
This is what I've tried so far:
class RoundedShadowCorners {
func shadowTopBar(_ topBar: UINavigationBar,_ offset: CGFloat,_ navigationItem: UINavigationItem){
topBar.isTranslucent = false
topBar.tintColor = UIColor.orange
topBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
topBar.shadowImage = UIImage()
topBar.backgroundColor = UIColor.white
let shadowView = UIView(frame: CGRect(x: 0, y: -offset, width: (topBar.bounds.width), height: (topBar.bounds.height) + offset))
shadowView.backgroundColor = UIColor.white
topBar.insertSubview(shadowView, at: 1)
let shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: shadowView.bounds, byRoundingCorners: [.bottomLeft , .bottomRight , .topLeft], cornerRadii: CGSize(width: 20, height: 20)).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
shadowLayer.shadowOpacity = 0.8
shadowLayer.shadowRadius = 2
shadowView.layer.insertSublayer(shadowLayer, at: 0)
topBar.prefersLargeTitles = true
topBar.topItem?.title = "HJFSKDJKA"
}
}
Problem with this is that this makes the title text be behind the actual NavigationBar and I can only make it come forward if I create a new UIView for the title and try positioning it on the screen, which makes it extremely difficult to be responsive.
offset isview.safeAreaInsets.top
I would prefer to do it without making a new UIView, because it makes things complicated, but I couldn't even begin to do it.
This is updated & tested code, there were certain lines which needs to be updated to overcome this behaviour,go through my in-line comments for details.
func shadowTopBar(_ topBar: UINavigationBar,_ offset: CGFloat){
// Set the prefers title style first
// since this is how navigation bar bounds gets calculated
//
topBar.prefersLargeTitles = true
topBar.topItem?.title = "HJFSKDJKA"
topBar.isTranslucent = false
topBar.tintColor = UIColor.orange
topBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
topBar.shadowImage = UIImage()
topBar.backgroundColor = UIColor.white
// Make your y position to the max of the uiNavigationBar
// Height should the cornerRadius height, in your case lets say 20
let shadowView = UIView(frame: CGRect(x: 0, y: topBar.bounds.maxY, width: (topBar.bounds.width), height: 20))
// Make the backgroundColor of your wish, though I have made it .clear here
// Since we're dealing it in the shadow layer
shadowView.backgroundColor = .clear
topBar.insertSubview(shadowView, at: 1)
let shadowLayer = CAShapeLayer()
// While creating UIBezierPath, bottomLeft & right will do the work for you in this case
// I've removed the extra element from here.
shadowLayer.path = UIBezierPath(roundedRect: shadowView.bounds, byRoundingCorners: [.bottomLeft , .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
// This too you can set as per your desired result
shadowLayer.shadowOffset = CGSize(width: 2.0, height: 4.0)
shadowLayer.shadowOpacity = 0.8
shadowLayer.shadowRadius = 2
shadowView.layer.insertSublayer(shadowLayer, at: 0)
}
Here's a detailed article on this. Medium

How to apply corner radius to certain corners and add shadow

Is there a way to apply rounded corners to certain corners only, and at the same time apply a shadow layer?
I tried doing that using the following extension, but that didn't work for me:
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat, shadow: Bool = false) {
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
if shadow {
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 2)
self.layer.shadowOpacity = 0.3
self.layer.shadowRadius = 3.0
}
}
}
So, I tossed in some code into playground and had a quick playground, based on the example from How to round only specific corners using maskedCorners
let view = UIView(frame: CGRect(x: 100, y: 100, width: 500, height: 500))
view.backgroundColor = UIColor.white
view.layer.cornerRadius = 15
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0)
view.layer.shadowOpacity = 1.0
view.layer.shadowRadius = 4
//view.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
view.layer.shouldRasterize = false
let outter = UIView(frame: CGRect(x: 0, y: 0, width: 700, height: 700))
outter.backgroundColor = UIColor.red
outter.addSubview(view)
outter
I did some testing:
With no corner radius
With masked corner radius
With reduced shawdowRadius
And just to be sure, increased corner radius

Can we have a rounded corner without applying masktobounds

Any possibility in having a rounded corner for a view container without using masksToBounds = true ?
signup.layer.cornerRadius = 10
signup.layer.masksToBounds = false
Yes, you can but you may have to write following code:
let containerView = UIView(frame: CGRect(x: 100.0, y: 100.0, width: 200.0, height: 200.0))
containerView.backgroundColor = UIColor.clear
let aPath = UIBezierPath(roundedRect: containerView.bounds, cornerRadius: 10.0)
aPath.lineWidth = 1.0
aPath.stroke()
let layer = CAShapeLayer()
layer.fillColor = UIColor.red.cgColor
layer.strokeColor = UIColor.red.cgColor
layer.path = aPath.cgPath
containerView.layer.addSublayer(layer)
self.view.addSubview(containerView)

Unable to set bottom shadow of UIView in iOS?

I want to set the bottom shadow of UIView.I am also setting corner radius.If i set maskToBounds to true then i am not able to set the shadow of UIView please tell how can i set both corner radius & shadow of UIView.
func addShadwToView(){
self.viewContainer.layer.masksToBounds = true;
self.viewContainer.layer.shadowRadius = 15;
self.viewContainer.layer.shadowOffset = CGSize(width: 0, height: 20)
self.viewContainer.layer.shadowOpacity = 0.5;
self.viewContainer.layer.cornerRadius = 5
self.viewContainer.layer.borderColor = UIColor.lightGray.cgColor
self.viewContainer.layer.borderWidth = 0.5
}
If you want both a corner radius and a drop shadow, so don't turn on -masksToBounds, but rather set the corner radius and set the bezier path of the shadow with a rounded rect. Keep the radius of the two the same:
Try it:
func addShadwToView(){
var borderLine = CAShapeLayer()
borderLine.path = UIBezierPath(roundedRect: frame2, byRoundingCorners: [.allCorners], cornerRadii: CGSize(width: 30, height: 0)).cgPath
borderLine.shadowColor = UIColor.white.cgColor
borderLine.shadowOffset = CGSize(width: 0, height: 1)
borderLine.shadowOpacity = 0.3
borderLine.shadowRadius = 10
self.viewContainer.layer.masksToBounds = true;
self.viewContainer.layer.cornerRadius = 5
self.viewContainer.layer.borderColor = UIColor.lightGray.cgColor
self.viewContainer.layer.borderWidth = 0.5
self.viewContainer.layer.addSublayer(borderLine)
}
Try this code...this will solve your problem
func addShadow(){
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: customView.frame.width, height: customView.frame.height))
customView.layer.shadowColor = UIColor.lightGray.cgColor
customView.layer.shadowOffset = CGSize(width: 0, height: 20)
customView.layer.shadowOpacity = 0.5
customView.layer.shadowRadius = 15
customView.layer.masksToBounds = false
customView.layer.shadowPath = shadowPath.cgPath
}

Shadow on UIView layer

I've make a path in order to mask my view:
let path = // create magic path (uiview bounds + 2 arcs)
let mask = CAShapeLayer()
mask.path = path.cgPath
view.layer.masksToBounds = false
view.layer.mask = mask
Up to here all ok.
Now I would like to add a shadow that follows path, is it possibile?
I try in several way, the last one is:
mask.shadowPath = path.cgPath
mask.shadowColor = UIColor.red.cgColor
mask.shadowOffset = CGSize(width: 10, height: 2.0)
mask.shadowOpacity = 0.5
But this produce a partial shadow and with color of the original view..
With debug view hierarchy:
Any advice?
Final result should be similar to this, but with shadow that "follows" arcs on path.
When you add a mask to a layer, it clips anything outside that mask - including the shadow. To achieve this you'll need to add a "shadow" view below your masked view, that has the same path as the mask.
Or add a shadow layer to the masked view's superview.
let view = UIView(frame: CGRect(x: 50, y: 70, width: 100, height: 60))
view.backgroundColor = .cyan
let mask = CAShapeLayer()
mask.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10).cgPath
view.layer.mask = mask
let shadowLayer = CAShapeLayer()
shadowLayer.frame = view.frame
shadowLayer.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10).cgPath
shadowLayer.shadowOpacity = 0.5
shadowLayer.shadowRadius = 5
shadowLayer.masksToBounds = false
shadowLayer.shadowOffset = .zero
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
container.backgroundColor = .white
container.layer.addSublayer(shadowLayer)
container.addSubview(view)
If you're going to be using this elsewhere, you could create a ShadowMaskedView that contains the shadow layer, and the masked view - maybe with a path property.
You can try this extension:
extension UIView {
func dropShadow() {
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: -1, height: 1)
self.layer.shadowRadius = 1
self.layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
self.layer.shouldRasterize = true
}
}

Resources