Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView with corner Radius and Shadow view doesn't clip subviews in corners

Below is the code for a custom Card View. The problem is, when I add the subviews to this in Interface builder it doesn't apply the corner radius to the subview. For the most part, I can get away with this by making subviews have a clear background color but I'm struggling with UIImageView. When I add that to a card it ends up with pointy corners and I've not been able to fix it.

Various solutions on here have suggested adding a second layer to display the shadow. I've attempted this but it still doesn't work as intended. What I'm trying to achieve is a view with rounded corners, drop shadow and adding any subviews (such as UIImageView) should also maintain the corner radius and not pointing out.

I've tried various settings with layer.masksToBounds and self.clipsToBounds and I always seem to get subviews with a corner radius but no shadow or the shadow visible and views not clipping.

@IBDesignable class CardView: UIView {

    @IBInspectable dynamic var cornerRadius: CGFloat = 6
    @IBInspectable dynamic var shadowOffsetWidth: Int = 2
    @IBInspectable dynamic var shadowOffsetHeight: Int = 2
    @IBInspectable dynamic var shadowColor: UIColor? = UIColor(netHex: 0x333333)
    @IBInspectable dynamic var shadowOpacity: Float = 0.5

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    override func layoutSubviews() {
        commonInit()
    }

    override func prepareForInterfaceBuilder() {
        commonInit()
    }

    func commonInit() {

        layer.cornerRadius = cornerRadius
        let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
        layer.masksToBounds = false

        layer.shadowColor = shadowColor?.cgColor
        layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
        layer.shadowOpacity = shadowOpacity
        layer.shadowPath = shadowPath.cgPath

        // This was how I tried to add a seperate shadow layer
//        let shadowView = UIView(frame: self.frame)
//        shadowView.layer.shadowColor = shadowColor?.cgColor
//        shadowView.layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
//        shadowView.layer.shadowOpacity = shadowOpacity
//        shadowView.layer.shadowPath = shadowPath.cgPath
//        shadowView.layer.masksToBounds = false
//
//        self.addSubview(shadowView)

    }

}
like image 564
StartPlayer Avatar asked Aug 01 '18 10:08

StartPlayer


People also ask

How do I change my corner radius to UIView?

If you start with a regular UIView it has square corners. You can give it round corners by changing the cornerRadius property of the view's layer . and smaller values give less rounded corners. Both clipsToBounds and masksToBounds are equivalent.

What is clip to bounds in Swift?

A Boolean value that determines whether subviews are confined to the bounds of the view.


2 Answers

The way you were trying to implement a second view to handle shadows is almost correct, you just didn't keep the right order.

Your CardView class already handles displaying a shadow. Leave that view as it is and instead add a UIView called "ContentView" as a subview. That content view has the same frame and corner radius as your CardView.

On the "ContentView", you don't need to do any work with shadows. Instead, set its layer's masksToBounds property to true. Now add all the content you want to display in your Card to the "ContentView" and it should clip correctly.

func commonInit() {

    layer.cornerRadius = cornerRadius
    let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
    layer.masksToBounds = false

    layer.shadowColor = shadowColor?.cgColor
    layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
    layer.shadowOpacity = shadowOpacity
    layer.shadowPath = shadowPath.cgPath

    let contentView = UIView()
    contentView.frame = self.frame
    contentView.layer.cornerRadius = cornerRadius
    contentView.layer.masksToBounds = true

    // any content you add should now be added to the contentView:
    // contentView.addSubview(aView)
}
like image 98
TomQDRS Avatar answered Oct 28 '22 23:10

TomQDRS


furthermore, you can specific corners.

layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]
like image 21
Zev003 Avatar answered Oct 28 '22 22:10

Zev003