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)
Related
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
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
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
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
}
}
I'm making a custom view that i want to mask and to add shadow to it.
the masking:
let p = UIBezierPath()
p.moveToPoint(CGPointMake(20, 20))
p.addLineToPoint(CGPointMake(100, 20))
p.addLineToPoint(CGPointMake(100, 50))
p.addLineToPoint(CGPointMake(110, 55))
p.addLineToPoint(CGPointMake(100, 60))
p.addLineToPoint(CGPointMake(100, 100))
p.addLineToPoint(CGPointMake(20, 100))
p.closePath()
let s = CAShapeLayer()
s.frame = layer.bounds
s.path = p.CGPath
s.fillColor = UIColor.greenColor().CGColor
layer.mask = s
the masking works, now i want to add a shadow.
but its not working.
i tried to add shadow to the main layer and nothing happens.
layer.shadowColor = UIColor.yellowColor().CGColor
layer.shadowRadius = 10
layer.shadowOpacity = 0.9
layer.shadowOffset = CGSizeZero
i tried to add it to the mask layer and i got the main view masked with a shadow.
s.shadowColor = UIColor.yellowColor().CGColor
s.shadowRadius = 10
s.shadowOpacity = 0.9
s.shadowOffset = CGSizeZero
Any suggestions how to add this yellow shadow to the masked view?
Thanks
Thanks #WilsonXJ
I changed mask to addSubLayer.
This is the answer that worked for me:
let p = UIBezierPath()
p.moveToPoint(CGPointMake(20, 20))
p.addLineToPoint(CGPointMake(100, 20))
p.addLineToPoint(CGPointMake(100, 50))
p.addLineToPoint(CGPointMake(110, 55))
p.addLineToPoint(CGPointMake(100, 60))
p.addLineToPoint(CGPointMake(100, 100))
p.addLineToPoint(CGPointMake(20, 100))
p.closePath()
let s = CAShapeLayer()
s.fillColor = UIColor.whiteColor().CGColor
s.frame = layer.bounds
s.path = p.CGPath
layer.backgroundColor = UIColor.clearColor().CGColor
layer.addSublayer(s)
layer.masksToBounds = true
layer.shadowColor = UIColor.yellowColor().CGColor
layer.shadowOffset = CGSizeZero
layer.shadowOpacity = 0.9
layer.shadowPath = p.CGPath
layer.shadowRadius = 10
I don't think that current answer is the right one, because there is no layer.mask usage anymore.
In case when you need to use layer.mask and to drop shadow of masked layer - the obvious solution is to add another layer below masked layer that will have same shape as layer.mask and drop it's shadow
example:
let view = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 500, height: 500)))
view.backgroundColor = .white
PlaygroundPage.current.liveView = view
let path: CGPath = ...
let maskedView = UIView(frame: path.boundingBox)
maskedView.center = view.center
maskedView.backgroundColor = .green
view.addSubview(maskedView)
let maskLayer = CAShapeLayer()
maskLayer.frame = maskedView.bounds
maskLayer.path = path
maskedView.layer.mask = maskLayer
let shadowLayer = CAShapeLayer()
shadowLayer.path = path
shadowLayer.frame = maskedView.frame
shadowLayer.shadowOpacity = 0.4
shadowLayer.shadowRadius = 2
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowOffset = CGSize(width: 4, height: 4)
maskedView.superview!.layer.insertSublayer(shadowLayer, below: maskedView.layer)