UIView with shadow and masked layer - ios

So I have an UIView (called myView) with some mask applied to it.
let maskPath = UIBezierPath(roundedRect: myView.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 12, height: 12))
let maskLayer = CAShapeLayer()
maskLayer.frame = myView.bounds
maskLayer.path = maskPath.cgPath
myView.layer.mask = maskLayer
Which layout this way:
What I am failing to do is add some shadow to myView. Since the view's layer have a mask, I am not able to add differents layers to it with shadow.
Does anyone ever had this problem?

set your image color to clear and to the following:
let maskPath = UIBezierPath(roundedRect: redView.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 12, height: 12))
let maskLayer = CAShapeLayer()[![enter image description here][1]][1]
maskLayer.fillColor = UIColor.red.cgColor // your color
maskLayer.frame = redView.bounds
maskLayer.path = maskPath.cgPath
redView.layer.addSublayer(maskLayer)
redView.layer.shadowOffset = CGSize(width: 10, height: 10)
redView.layer.shadowColor = UIColor.green.cgColor
redView.layer.shadowOpacity = 1

Related

Add a shadow with CAShapeLayer?

I have a CAShapeLayer with bottom rounded corners only. What I want is to add a shadow at the bottom, but it's not working, even though the code looks obviously right. All it does it round the bottom corners but I don't see a shadow.
let shapeLayer = CAShapeLayer()
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
shapeLayer.path = path
shapeLayer.shadowColor = UIColor(r: 233, g: 233, b: 233).cgColor
shapeLayer.shadowOffset = CGSize(width: 0.0, height: 2.8)
shapeLayer.shadowOpacity = 1.0
shapeLayer.shadowRadius = 0.0
shapeLayer.shouldRasterize = true
shapeLayer.rasterizationScale = UIScreen.main.scale
layer.rasterizationScale = UIScreen.main.scale
layer.mask = shapeLayer
The shapeLayer is rounding the corners of your view because it's set as the layer's mask. You probably want to add it as a sublayer instead.
Old:
layer.mask = shapeLayer
New:
layer.addSublayer(shapeLayer)
I wrote another answer about adding a shadow to a view with rounded corners here if it helps: https://stackoverflow.com/a/41475658/6658553
Another solution,
The following UIView extension will work with any shape you mask the view with
extension UIView {
func addShadow() {
self.backgroundColor = UIColor.clear
let roundedShapeLayer = CAShapeLayer()
let roundedMaskPath = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft, .bottomLeft, .bottomRight],
cornerRadii: CGSize(width: 8, height: 8))
roundedShapeLayer.frame = self.bounds
roundedShapeLayer.fillColor = UIColor.white.cgColor
roundedShapeLayer.path = roundedMaskPath.cgPath
self.layer.insertSublayer(roundedShapeLayer, at: 0)
self.layer.shadowOpacity = 0.4
self.layer.shadowOffset = CGSize(width: -0.1, height: 4)
self.layer.shadowRadius = 3
self.layer.shadowColor = UIColor.lightGray.cgColor
}
}

Bottom Corners are not working in swift

I try to set topright & bottomRight corner as follow but it is not working
Try 1
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: self.main_scroll.bounds,
byRoundingCorners: [.TopRight, .BottomRight],
cornerRadii: CGSize(width: 10.0, height: 10.0)).CGPath
self.main_scroll.layer.mask = maskLayer
Try 2
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: self.main_scroll.frame, byRoundingCorners: UIRectCorner.BottomRight.union(.TopRight), cornerRadii: CGSizeMake(100, 100)).CGPath
self.main_scroll.layer.mask = maskLayer
Try 3
let rectShape = CAShapeLayer()
rectShape.bounds = self.main_scroll.frame
rectShape.position = self.main_scroll.center
rectShape.path = UIBezierPath(roundedRect: self.main_scroll.bounds, byRoundingCorners: [.BottomRight,.TopRight], cornerRadii: CGSize(width: 500, height: 500)).CGPath
self.main_scroll.layer.mask = rectShape
Thank you,
Try this using DispatchQueue.main.async this works for me
DispatchQueue.main.async {
let path = UIBezierPath(roundedRect: self.messageLabel.bounds,
byRoundingCorners:[.bottomRight, .topRight, .topLeft],
cornerRadii: CGSize(width: 8, height: 8))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
self.messageLabel.layer.mask = maskLayer
}

How to get rounded corners only for topLeft and bottomLeft corners?

I've followed other stackoverflow threads and created CAShapeLayer and added it to button layer.
let bazierPath = UIBezierPath.init(roundedRect: button.bounds, byRoundingCorners: [UIRectCorner.bottomLeft, UIRectCorner.topLeft], cornerRadii: CGSize(width: 10.0, height: 10.0 ))
let shapeLayer = CAShapeLayer()
shapeLayer.frame = button.bounds
shapeLayer.path = bazierPath.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 1.0
button.layer.mask = shapeLayer
but the problem is I'm getting corners with clear color but i want them to be green. check for buttons with "Mon" and "Fri" in following image for clear understanding about problem.
in your code you forgot to add the addSublayer to main layer contentView.layer.addSublayer(subLayer) for sample purpose
let contentView = UIView(frame: CGRect(x: CGFloat(50), y: CGFloat(100), width: CGFloat(200), height: CGFloat(100)))
self.view.addSubview(contentView)
let subLayer = CAShapeLayer()
subLayer.fillColor = UIColor.blue.cgColor
subLayer.strokeColor = UIColor.green.cgColor
subLayer.lineWidth = 1.0
subLayer.path = UIBezierPath(roundedRect: contentView.bounds, byRoundingCorners: [UIRectCorner.bottomLeft, UIRectCorner.topLeft], cornerRadii: CGSize(width: 10.0, height: 10.0 )).cgPath
contentView.layer.addSublayer(subLayer)
output
Try to use the following function:
fun createRoundCorners(corners:UIRectCorner, radius: CGFloat)
{
let borderLayer = CAShapeLayer()
borderLayer.frame = self.layer.bounds
borderLayer.strokeColor = // Add your color
borderLayer.fillColor = UIColor.clearColor().CGColor
borderLayer.lineWidth = 1.0
let path = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
borderLayer.path = path.CGPath
self.layer.addSublayer(borderLayer);
}

Set corner radius to particular side of UIView/UIButton only

I want to set corner radius to my UI element as shown in attached snap:
I tried setting corner radius using UIBezierPath but it doesn't give expected result. I also added those three UIButtons in UIView and set corner radius to Container view but no use.
With reference to https://stackoverflow.com/a/31233171/4886069 , Following is the code what I've tried on top and bottom buttons:
let rectShape = CAShapeLayer()
rectShape.bounds = self.scanButton.frame
rectShape.position = self.scanButton.center
rectShape.path = UIBezierPath(roundedRect: self.scanButton.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
self.scanButton.layer.mask = rectShape
let rectShape1 = CAShapeLayer()
rectShape1.bounds = self.manualButton.frame
rectShape1.position = self.manualButton.center
rectShape.path = UIBezierPath(roundedRect: self.manualButton.bounds, byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
self.manualButton.layer.mask = rectShape1
Following is the code what I've tried on container view:
containerView.layer.cornerRadius = 20
following is the output what I got:
Any help appreciated. Thank you
Use topRight, bottomLeft, and bottomRight in byRoundingCorners: [.topLeft] option for other corner for your expected result.
This code is for swift 2.0 just convert this code into swift 3.0
cancelbnutton.layer.cornerRadius = 10.0
cancelbnutton.clipsToBounds = true
let rectShape = CAShapeLayer()
rectShape.bounds = scanbutton.frame
rectShape.position = scanbutton.center
rectShape.path = UIBezierPath(roundedRect: scanbutton.bounds, byRoundingCorners: [UIRectCorner.TopLeft , UIRectCorner.TopRight], cornerRadii: CGSizeMake(10, 10)).CGPath
scanbutton.layer.mask = rectShape
let rectShape1 = CAShapeLayer()
rectShape1.bounds = manualbutton.frame
rectShape1.position = manualbutton.center
rectShape1.path = UIBezierPath(roundedRect: manualbutton.bounds, byRoundingCorners: [UIRectCorner.BottomLeft , UIRectCorner.BottomRight], cornerRadii: CGSizeMake(10, 10)).CGPath
manualbutton.layer.mask = rectShape1
Use this for giving Corner radius
let Path = UIBezierPath(roundedRect: button.bounds, byRoundingCorners: [UIRectCorner.TopLeft , UIRectCorner.BottomLeft], cornerRadii: CGSizeMake(1.0, 1.0)).CGPath

Round Top Corners of a UIButton in Swift

I know I can round all four corners using:
myBtn.layer.cornerRadius = 8
myBtn.layer.masksToBounds = true
Since I only want to round two, I did some research and found this:
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
}
}
Which is called like this:
view.roundCorners([.TopLeft , .TopRight], radius: 10)
Yet this doesn't work for a UIButton. When I switch the extension to be for type UIButton and pass it a button , the output looks like this:
The question is, how do I adapt this to work on a UIButton?
Swift 4: For latest iOS 11 onwards
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 11.0, *) {
self.viewToRound.clipsToBounds = true
viewToRound.layer.cornerRadius = 20
viewToRound.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
// Fallback on earlier versions
}
}
Earlier iOS (10,9 etc) Versions (works for iOS 11 too)
override func viewDidLayoutSubviews() {
self.viewToRound.clipsToBounds = true
let path = UIBezierPath(roundedRect: viewToRound.bounds,
byRoundingCorners: [.topRight, .topLeft],
cornerRadii: CGSize(width: 20, height: 20))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
viewToRound.layer.mask = maskLayer
}
Adding Extension of UIButton:
extension UIButton{
func roundedButton(){
let maskPath1 = UIBezierPath(roundedRect: bounds,
byRoundingCorners: [.topLeft , .topRight],
cornerRadii: CGSize(width: 8, height: 8))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = bounds
maskLayer1.path = maskPath1.cgPath
layer.mask = maskLayer1
}
}
Calling in viewDidAppear/viewDidLayoutSubviews:
btnCorner.roundedButton()
Button Corner OutPut:
For swift 3 Kirit Modi's answer is changed to:
extension UIButton {
func roundedButton(){
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft , .topRight],
cornerRadii:CGSize(width:8.0, height:8.0))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
}
At the start of the extension's file don't forget to add:
import UIKit
If you want an extension for a UIView with the option of rounding top or bottom corners you can use:
extension UIView {
func roundedCorners(top: Bool){
let corners:UIRectCorner = (top ? [.topLeft , .topRight] : [.bottomRight , .bottomLeft])
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: corners,
cornerRadii:CGSize(width:8.0, height:8.0))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
}
Which is called for a button as:
myButton.roundedCorners(top: true)
iOS 11 has made it really easy to round corners. The code below rounds the top left and bottom right corners.
myView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]
For swift 5 and the most flexibility
Define an extension with a roundCorners function
extension UIButton {
func roundCorners(corners: UIRectCorner, radius: Int = 8) {
let maskPath1 = UIBezierPath(roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = bounds
maskLayer1.path = maskPath1.cgPath
layer.mask = maskLayer1
}
}
Call the roundCorners function
myButton.roundCorners(corners: [.topLeft, .topRight])
Or with a specific radius
myButton.roundCorners(corners: [.topLeft, .topRight], radius: 20)
Update you extension to be like this:
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()
let rect = self.bounds
mask.frame = rect
mask.path = path.cgPath
self.layer.mask = mask
}
}
The shape layer (mask) needs to know the frame
Use this Code,
let path = UIBezierPath(roundedRect:viewTo.bounds, byRoundingCorners:[.TopRight, .TopLeft], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()
maskLayer.path = path.CGPath
viewTo.layer.mask = maskLayer
hope its helpful
That's what helped me
extension UIView {
func roundCorners(
corners: UIRectCorner,
radius: CGFloat
) {
let path = UIBezierPath(
roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(
width: radius,
height: radius
)
)
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
Pay attention to the fact that if you have layout constraints attached to it, you must refresh this as follows in your UIView subclass:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
yourButtonOutletName.roundCorners(
corners: [.topLeft, .topRight],
radius: yourButtonOutletName.frame.height / 2
)
}
You forgot to set the frame of your shape layer:
mask.frame = layer.bounds
rounding is applied to corner's of view/Button .. , but coming to border of button , it is not applying correctly. can I have any solution for that? #
Here is the code that I have used , which is working (border) in iOS11.0 and above and not in below versions(<11.0)
if #available(iOS 11.0, *) {
self.layer.cornerRadius = radius
self.layer.maskedCorners = maskedCorners
} else {
let shapeLayer = CAShapeLayer()
shapeLayer.position = self.center
self.layer.masksToBounds = true
self.clipsToBounds = true
let bezirePath = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
shapeLayer.bounds = frame
shapeLayer.path = bezirePath.cgPath
self.layer.mask = shapeLayer

Resources