Swift - Shadow for a irregular shape of a View - ios

i am struggling to add shadow to a custom shape.
Here is a picture of what i want to construct:
(Dont mind the text and the symbol)
You can see the custom shape with the curved corner on the right and the rectangular shape on the left with shadow.
I am using UIView, and added corner to the left.
This is the code i have so far that shape the view correct:
View1.backgroundColor = .green //green color is just to see the shape well
let path = UIBezierPath(roundedRect:View1.bounds,
byRoundingCorners:[.topRight, .bottomRight],
cornerRadii: CGSize(width: self.frame.height/2, height: self.frame.height/2))
let maskLayer = CAShapeLayer()
I Have tried to add shadow to it, but the shadow does not apear.
Here is the code i have tried to add shadow:
View1.layer.masksToBounds = false
View1.layer.layer.shadowPath = maskLayer.path
View1.layer.shadowColor = UIColor.black.cgColor
View1.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
View1.layer.shadowOpacity = 0.5
View1.layer.shadowRadius = 1.0
How can you add shadow to this shape?

You can achieve this by using a single UIView (shadowView), adding a shapeLayer sublayer and setting the shadow of the shadowView's layer.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
#IBOutlet var shadowView: UIView!
func setup() {
// setup irregular shape
let path = UIBezierPath.init(roundedRect: shadowView.bounds, byRoundingCorners: [.topRight, .bottomRight], cornerRadii: CGSize.init(width: 20, height: 20))
let layer = CAShapeLayer.init()
layer.frame = shadowView.bounds
layer.path = path.cgPath
layer.fillColor = UIColor.white.cgColor
layer.masksToBounds = true
shadowView.layer.insertSublayer(layer, at: 0)
// setup shadow
shadowView.layer.shadowRadius = 8
shadowView.layer.shadowOpacity = 0.2
shadowView.layer.shadowOffset = CGSize.init(width: 0, height: 2.5)
shadowView.layer.shadowColor = UIColor.black.cgColor
shadowView.layer.shadowPath = path.cgPath
}
}
Note:
The shadowView.clipToBounds must be false for the shadows to take effect.
To see the layer.fillColor, set the shadowView.backgroundColor to .clear.
You can easily achieve the above via Interface Builder by setting the 'Background' property and unchecking the 'Clip to Bounds' checkbox.

Related

UIView Custom Class trigger either shadow or roundCorners/addCornerRadius but not both in swift 3 [duplicate]

This question already has answers here:
UIView with rounded corners and drop shadow?
(35 answers)
Closed 4 years ago.
UIView Custom Class trigger only shadow. I want to display both shadow and rounded corner for specific edges.Here is my code. Any suggestions would be appreciated.
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
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
}
}
class CustomView: UIView {
override func layoutSubviews() {
self.shadowColor = UIColor.darkGray
self.shadowOffset = CGSize(width: 4, height: 4)
self.shadowOpacity = 0.5
self.shadowRadius = 6.0
//only rounded corners triggered in UI
self.backgroundColor = UIColor.white
self.roundCorners(corners: [.topRight, .bottomRight], radius: 15.0)
}
}
Thanks in advance
Use separate views for the shadow and the border. The base view is transparent and has the shadow. The border view clips any other subcontent that it has to its borders. See the code below.
// add the shadow to the base view
baseView.backgroundColor = UIColor.clear
baseView.layer.shadowColor = UIColor.black.cgColor
baseView.layer.shadowOffset = CGSize(width: 3, height: 3)
baseView.layer.shadowOpacity = 0.7
baseView.layer.shadowRadius = 4.0
// add the border to subview
let borderView = UIView()
borderView.frame = baseView.bounds
borderView.layer.cornerRadius = 10
borderView.layer.borderColor = UIColor.black.cgColor
borderView.layer.borderWidth = 1.0
borderView.layer.masksToBounds = true
baseView.addSubview(borderView)
// add any other subcontent that you want clipped
let otherSubContent = UIImageView()
otherSubContent.image = UIImage(named: "lion")
otherSubContent.frame = borderView.bounds
borderView.addSubview(otherSubContent)
Use this function:-
func addShadow(view: UIView, corner: CGFloat) {
view.layer.shadowColor = UIColor.gray.cgColor
view.layer.shadowOpacity = 0.5
view.layer.shadowRadius = 2
view.layer.shadowOffset = CGSize(width: 1, height: 1)
view.layer.masksToBounds = false
view.layer.cornerRadius = corner
}

How To Add Border To a UIView With Mask?

Here's what I need to do to my two buttons.
and here's what I got right now.
What I'm doing:
- Set these up in IB inside a stackView.
- Add masks
- Add borders with width and color
- Add shadow.
The borders are being added but being cut-off as well by the masks.
Codes:
public func addCornerRadiusToCorners(
_ corners: UIRectCorner,
cornerRadius: CGFloat,
borderColor: UIColor,
borderWidth: CGFloat) {
// self.layer.masksToBounds = true
// self.clipsToBounds = true
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.cgColor
let size = CGSize(width: cornerRadius, height: cornerRadius)
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size)
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
public func addDefaultShadow() {
let shadowPath = UIBezierPath(rect: self.bounds)
self.layer.masksToBounds = false
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shadowOpacity = 0.5
self.layer.shadowPath = shadowPath.cgPath
}
Any idea how to achieve the result in the first photo?
EDIT: the border is being cut, the result was just got from the UI Debugger of Xcode. Sorry.
Add corner radius to you view on viewDidAppear
#IBOutlet weak var segmentView: UIView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
segmentView.layer.cornerRadius = segmentView.frame.size.height/2;
segmentView.layer.borderWidth = 1.0;
segmentView.clipsToBounds = true
segmentView.layer.borderColor = UIColor.lightGray.cgColor
segmentView.layer.shadowColor = UIColor.black.cgColor
segmentView.layer.shadowOpacity = 0.2
segmentView.layer.shadowRadius = 10.0
segmentView.layer.shadowOffset = CGSize(width: 1, height: 1)
segmentView.layer.masksToBounds = false
}
Sample Output:
you forgot to clip ur view.
self.clipsToBounds = true
you need to set maskstobounds to the view layer
segmentView.layer.masksToBounds = true

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
}
}

Chopped edge around border of image

I have an image with a border
let smallicon: UIImageView = {
let smallicon = UIImageView()
smallicon.layer.borderWidth = 2
smallicon.layer.borderColor = UIColor.whiteColor().CGColor
smallicon.hidden = true
return smallicon
}()
The problem is that is has tiny chopped edge around border (small image with yellow and black lines)
How to get rid of it ?
Solution
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Create a view with red background for demonstration
let v = UIView(frame: CGRectMake(0, 0, 100, 100))
v.center = view.center
v.backgroundColor = UIColor.redColor()
view.addSubview(v)
// Add rounded corners
let maskLayer = CAShapeLayer()
maskLayer.frame = v.bounds
maskLayer.path = UIBezierPath(roundedRect: v.bounds, byRoundingCorners: .TopRight | .TopLeft, cornerRadii: CGSize(width: 25, height: 25)).CGPath
v.layer.mask = maskLayer
// Add border
let borderLayer = CAShapeLayer()
borderLayer.path = maskLayer.path // Reuse the Bezier path
borderLayer.fillColor = UIColor.clearColor().CGColor
borderLayer.strokeColor = UIColor.greenColor().CGColor
borderLayer.lineWidth = 5
borderLayer.frame = v.bounds
v.layer.addSublayer(borderLayer)
}
}
NOTE! in order this to work target view must have frame property setup with size. without size your view will not be seen at all

How to add a shadow and CAShapeLayer to the same UIView

I'm trying to add a 6px radius to the bottom left and right corners of my view innerView, as well as a shadow to the whole of innerView. innerView is enclosed in a view called view2.
The only way I can get this to work is by using the following code:
let containerLayer = CALayer()
containerLayer.shadowColor = UIColor.blackColor().CGColor
containerLayer.shadowOffset = CGSizeMake(1, 2)
containerLayer.shadowOpacity = 0.2
containerLayer.shadowRadius = 2
let maskPath = UIBezierPath(roundedRect: innerView.bounds, byRoundingCorners: UIRectCorner.BottomLeft.union(.BottomRight), cornerRadii: CGSize(width: 6, height: 6)).CGPath
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath
innerView.layer.mask = maskLayer
containerLayer.addSublayer(innerView.layer)
view2.layer.addSublayer(containerLayer)
However, since view2 is actually the contentView of a UITableViewCell, this code disables scrolling of the UITableView when dragging on containerLayer...
Swift 4 :
This Code Helped Me To Drop A Shadow To UIVIEW And Add CornerRadious Using UIBezierPath. Set Your View's Background Color As ClearColor
yourView.backgroundColor=UIColor.clear
let path1 = UIBezierPath(roundedRect:yourView.bounds,
byRoundingCorners:[.topRight, .bottomLeft],
cornerRadii: CGSize(width: 20, height: 20))
let maskLayer1 = CAShapeLayer()
maskLayer1.path = path1.cgPath
maskLayer1.fillColor = UIColor.white.cgColor
maskLayer1.shadowColor = UIColor.darkGray.cgColor
maskLayer1.shadowPath = maskLayer1.path
maskLayer1.shadowOffset = CGSize(width: 0.0, height: 2.0)
maskLayer1.shadowOpacity = 0.6
maskLayer1.shadowRadius = 2
yourView.layer.insertSublayer(maskLayer1, at: 0)
Solved it by embedding innerView in another view. I applied the shadow to the new view and the radius to the innerView:
func addShadowTo(shadowView: UIView, andRadiusTo radiusView: UIView) {
let maskPath = UIBezierPath(roundedRect: radiusView.bounds, byRoundingCorners: UIRectCorner.BottomLeft.union(.BottomRight), cornerRadii: CGSize(width: 6, height: 6)).CGPath
shadowView.layer.shadowPath = maskPath
shadowView.layer.shadowColor = UIColor.blackColor().CGColor
shadowView.layer.shadowOffset = CGSizeMake(1, 2)
shadowView.layer.shadowOpacity = 0.2
shadowView.layer.shadowRadius = 2
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath
radiusView.layer.mask = maskLayer
}
If you are using Autolayout, then remove UIBezierPath. Try this code and custom one for yourself:
//This code custom a view with shadow and corner radius = 6
let shadowLayer: CALayer = shadowView.layer
shadowView.clipsToBounds = false
shadowLayer.shadowColor = 'your color'
shadowLayer.shadowOffset = CGSizeZero
shadowLayer.shadowOpacity = 1
shadowLayer.shadowRadius = 3
shadowLayer.shouldRasterize = true
let innerLayer: CALayer = innerView.layer
innerLayer.cornerRadius = 6
innerView.clipsToBounds = true
innerLayer.borderColor = 'your color'.CGColor
innerLayer.borderWidth = 1
The "innerView" is inside "shadowView" (on my xib file).

Resources