0

I need to round the bottom left and right corners of a view and base on what I found on the internet I created this function inside an UIview extension:

func setRoundedCorner(withRadius radius: CGSize, forCorners corners: UIRectCorner ) {

    let viewShape = CAShapeLayer()
    viewShape.bounds = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.bounds.width, height: self.bounds.height)
    viewShape.position = self.center
    viewShape.path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: radius).cgPath
    self.layer.mask = viewShape

}

Now, I encountered two issues with this solution; despite it works perfectly for a static view, my view changes its height programmatically causing the view to be truncated; I found a workaround to that issue recalling each time the setRoundedCorner method, but it's very uncomfortable inside my view; is there any other way to solve this? Another issue harder to me to solve is the shadow set. I usually use this method:

func setShadow(height: CGFloat, width: CGFloat, opacity: Float, radius: CGFloat) {
    self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
    self.layer.shadowColor = UIColor.black.cgColor
    self.layer.shadowOpacity = opacity
    self.layer.shadowOffset = CGSize(width: width, height: height)
    self.layer.cornerRadius = radius
    self.layer.masksToBounds = false
}

Instead the view appears this way when it should has the shadow under itself:

enter image description here

But in this situation doesn't seem to work not showing any shadow at all. Can anyone give me some advice?

Lorenzo Santini
  • 655
  • 7
  • 13

2 Answers2

1

Here, I'm sharing with you my category for adding corner radius (using BezierPath) and at the same time supporting adding a shadow.

import UIKit

/// A category for all views.
extension UIView {
    /// Setups the layer of a view.
    public func setupLayer(
        cornerRadius: CGFloat = 22,
        borderWidth: CGFloat = 0,
        borderColor: UIColor = .clear,
        shadowOffSet: CGSize = CGSize(width: 0, height: 1),
        shadowColor: UIColor = UIColor(red:0, green:0, blue:0, alpha:0.15),
        shadowOpacity: Float = 1,
        shadowRadius: CGFloat = 2,
        shouldClipToBounds: Bool = false
        ) {

        self.layer.cornerRadius     = cornerRadius
        self.layer.borderWidth      = borderWidth
        self.layer.borderColor      = borderColor.cgColor
        self.layer.shadowOffset     = shadowOffSet
        self.layer.shadowColor      = shadowColor.cgColor
        self.layer.shadowOpacity    = shadowOpacity
        self.layer.shadowRadius     = shadowRadius
        self.clipsToBounds = shouldClipToBounds
    }

    /// Round the corner radius with bezier path.
    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
    }
}

Feel free to edit everything on this. Now, let's talk about your problem. By the way, this code, I use this in my production projects. Sometimes you need to round your corners in your viewDidLayoutSubviews, viewWillLayoutSubviews. And if you're using a subclass of a UIView or UITableViewCell or similar classes, then you add your rounding corners code inside their draw functions.

override func draw(_ rect: CGRect) {
        super.draw(rect)
    // Round corners here...
}

enter image description here

Finally, for setting up the layers, I usually call it inside the lazy init declaration of my controls (e.g. buttons, textFields...). Know the difference of adding corners using bezierPath and by simply layer.cornerRadius. :)

I hope this helps!

Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
  • Ok thank u a lot...I’m gonna try...just a couple of questions...can u explain me the difference between using layer.cornerRadius and bezierPath? I used the last one cause I needed to set only the bottom corners...and another question: what do u mean by the draw function of the custom class? – Lorenzo Santini Oct 15 '18 at 17:13
  • Well the layer.cornerRadius does not need to be called inside the ```draw``` / ```viewWillLayoutSubviews``` method. And also you are rounding all the corners using that. While using the BezierPath allows you to round any corner. It can be all corners, topLeft, topRight only, bottomRight and topRight, etc... ```draw``` method (in cells, or UIView) is the equivalent of ```viewWillLayoutSubviews``` of the controllers. Don't forget to select an answer to close your questions. Open a new thread if you want to ask further questions (but make sure it isn't a duplicate) :) – Glenn Posadas Oct 15 '18 at 17:18
  • Yeah...thank you very much...I’ll try your code and then if I solved my issue I’ll select your answer – Lorenzo Santini Oct 15 '18 at 17:22
  • I tried your code but it doesn't seem to work...there isn't any shadow – Lorenzo Santini Oct 15 '18 at 19:29
  • How did you implement it? Which one? See my edited answer, I'll post a sample output. – Glenn Posadas Oct 16 '18 at 03:32
  • I simply called the setupLayer() and roundCorners() inside the viewDidLoad methods and it rounds the corners but it doesn't show any shadow. As I said in another comment...Could it be related to the fact that this view has other elements inside itself and it's a custom view? – Lorenzo Santini Oct 16 '18 at 19:39
  • Did you adjust the values to satisfy your requirements? Like shadow opacity, shadow radius? Try setupLayer() first without the roundCorners(). And remember that roundCorners() which uses BezierPath should be inside the ```draw``` or ```viewWillLayoutSubviews``` – Glenn Posadas Oct 17 '18 at 03:30
  • If I call the method setupLayer() alone inside the ViewController It does work perfectly showing the shadow and everything but if I also call the roundCorners() method inside the draw method (I point we're talking of a UIView subclass) it rounds the corners but the shadow disappear – Lorenzo Santini Oct 17 '18 at 20:14
0

You can simply add this extension

extension UIView 
{ 
func dropShadow(scale: Bool = true) {
    layer.masksToBounds = false
    layer.shadowColor = UIColor.black.cgColor
    layer.shadowOpacity = 0.5
    layer.shadowOffset = CGSize(width: -1, height: 1)
    layer.shadowRadius = 1

    layer.shadowPath = UIBezierPath(rect: bounds).cgPath
    layer.shouldRasterize = true
    layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}

func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
    layer.masksToBounds = false
    layer.shadowColor = color.cgColor
    layer.shadowOpacity = opacity
    layer.shadowOffset = offSet
    layer.shadowRadius = radius

    layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
    layer.shouldRasterize = true
    layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}


 }

After add above extension you can use like below

  priceview.layer.cornerRadius = 5
  priceview.dropShadow(color: UIColor.darkGray, opacity: 0.5, offSet: CGSize(width: 1, height: 1), radius: 3, scale: true)

I hope it wil help you.

Virani Vivek
  • 888
  • 1
  • 8
  • 22