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)
}
}
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.
A Boolean value that determines whether subviews are confined to the bounds of the view.
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)
}
furthermore, you can specific corners.
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With