I have to create a custom UIView
that will have round corners, a border, a shadow and its drawRect()
method is overridden to provide custom drawing
Shadow is dropped from whatever is inside view's layer. When you disable clipping, entire layer rectangle gets filled with default backgroundColor
so the shadow becomes rectangular too. Instead of clipping it with rounded mask just make layer's contents rounded, draw them yourself. And layer
's border is drawn around its bounds, so you need to draw it yourself too.
For example, in backgroundColor
setter set actual background color to clearColor
and use passed color in drawRect
to draw a rounded rect with.
In example below I declare properties as IBInspectable
and the whole class as IBDesignable
, so everything can be set in storyboard. This way you can even use default Background selector to change your rounded rect color.
@IBDesignable class RoundRectView: UIView {
@IBInspectable var cornerRadius: CGFloat = 0.0
@IBInspectable var borderColor: UIColor = UIColor.blackColor()
@IBInspectable var borderWidth: CGFloat = 0.5
private var customBackgroundColor = UIColor.whiteColor()
override var backgroundColor: UIColor?{
didSet {
customBackgroundColor = backgroundColor!
super.backgroundColor = UIColor.clearColor()
}
}
func setup() {
layer.shadowColor = UIColor.blackColor().CGColor;
layer.shadowOffset = CGSizeZero;
layer.shadowRadius = 5.0;
layer.shadowOpacity = 0.5;
super.backgroundColor = UIColor.clearColor()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
override func drawRect(rect: CGRect) {
customBackgroundColor.setFill()
UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius ?? 0).fill()
let borderRect = CGRectInset(bounds, borderWidth/2, borderWidth/2)
let borderPath = UIBezierPath(roundedRect: borderRect, cornerRadius: cornerRadius - borderWidth/2)
borderColor.setStroke()
borderPath.lineWidth = borderWidth
borderPath.stroke()
// whatever else you need drawn
}
}
@IBDesignable class RoundedView: UIView {
@IBInspectable var cornerRadius: CGFloat = 0.0
@IBInspectable var borderColor: UIColor = UIColor.black
@IBInspectable var borderWidth: CGFloat = 0.5
private var customBackgroundColor = UIColor.white
override var backgroundColor: UIColor?{
didSet {
customBackgroundColor = backgroundColor!
super.backgroundColor = UIColor.clear
}
}
func setup() {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize.zero
layer.shadowRadius = 5.0
layer.shadowOpacity = 0.5
super.backgroundColor = UIColor.clear
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
override func draw(_ rect: CGRect) {
customBackgroundColor.setFill()
UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius ?? 0).fill()
let borderRect = bounds.insetBy(dx: borderWidth/2, dy: borderWidth/2)
let borderPath = UIBezierPath(roundedRect: borderRect, cornerRadius: cornerRadius - borderWidth/2)
borderColor.setStroke()
borderPath.lineWidth = borderWidth
borderPath.stroke()
// whatever else you need drawn
}
}
IB_DESIGNABLE
@interface RoundRectView : UIView
@property IBInspectable CGFloat cornerRadius;
@property IBInspectable UIColor *borderColor;
@property IBInspectable CGFloat borderWidth;
@end
@interface RoundRectView()
@property UIColor *customBackgroundColor;
@end
@implementation RoundRectView
-(void)setup{
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowRadius = 5.0;
self.layer.shadowOpacity = 0.5;
[super setBackgroundColor:[UIColor clearColor]];
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setup];
}
return self;
}
-(void)setBackgroundColor:(UIColor *)backgroundColor{
self.customBackgroundColor = backgroundColor;
super.backgroundColor = [UIColor clearColor];
}
-(void)drawRect:(CGRect)rect{
[self.customBackgroundColor setFill];
[[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.cornerRadius] fill];
CGFloat borderInset = self.borderWidth/2;
CGRect borderRect = CGRectInset(self.bounds, borderInset, borderInset);
UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:borderRect cornerRadius:self.cornerRadius - borderInset];
[self.borderColor setStroke];
borderPath.lineWidth = self.borderWidth;
[borderPath stroke];
// whatever else you need drawn
}
@end
Result
Try this it is work for me...
yourView.layer.shadowColor = UIColor.blackColor().CGColor
yourView.layer.shadowOpacity = 0.5
yourView.layer.shadowOffset = CGSize(width: 3, height: 3)
yourView.layer.shadowRadius = 05
yourView.layer.shadowPath = UIBezierPath(rect: yourView.bounds).CGPath
yourView.layer.shouldRasterize = true
In Swift. What did work for me was adding:
self.noteImage.layer.masksToBounds = false
So, the full code is:
self.noteImage.layer.masksToBounds = false
self.noteImage.layer.shadowColor = UIColor.redColor().CGColor
self.noteImage.layer.shadowOpacity = 0.5
self.noteImage.layer.shadowOffset = CGSize(width: 2, height: 2)
self.noteImage.layer.shadowRadius = 1
self.noteImage.layer.shadowPath = UIBezierPath(rect: noteImage.bounds).CGPath
self.noteImage.layer.shouldRasterize = true
This is my solution. If you have multiple type of views like UIView, UIControl, UITableView and so on, and don't want to make subclasses of each of them, or you want to add this effect with smallest changes to your code, then this might be what you are looking for.
Objective-C.h
#import <UIKit/UIKit.h>
@interface UIView (CornerAndShadow)
- (void)setCornerAndShadow;
@end
Objective-C.m
#import "UIView+CornerAndShadow.h"
#import <Masonry.h>
@implementation UIView (CornerAndShadow)
- (void)setCornerAndShadow {
// constants
CGFloat fCornerRadius = 9.f;
// only work for views with superview
if (self.superview == nil) {
return;
}
// set corner
self.layer.cornerRadius = fCornerRadius;
self.layer.masksToBounds = YES;
// create and configure shadowView
UIView *shadowView = [UIView new];
shadowView.backgroundColor = self.backgroundColor; // just to make shadow visible
shadowView.layer.cornerRadius = fCornerRadius;
shadowView.layer.shadowColor = [UIColor redColor].CGColor;
shadowView.layer.shadowOffset = CGSizeMake(0, 3.f);
shadowView.layer.shadowOpacity = 0.5f;
shadowView.layer.shadowRadius = 5.f;
// put shadowView into superview right below self
[self.superview insertSubview:shadowView belowSubview:self];
// set shadowView's frame equal to self
[shadowView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
// use this if you're not using autolayout, and can get real frame here
// shadowView.frame = self.frame;
}
@end
In Swift 4.1. For making rounded corner of UIView I have created Extension of UIView as follow.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var viewOuter: UIView!
@IBOutlet weak var viewInner: UIView!
override func viewDidLoad() {
super.viewDidLoad()
viewOuter.backgroundColor = UIColor.clear
viewInner.roundCorners(15.0)
viewOuter.addViewShadow()
}
}
extension UIView {
public func roundCorners(_ cornerRadius: CGFloat) {
self.layer.cornerRadius = cornerRadius
self.clipsToBounds = true
self.layer.masksToBounds = true
}
public func addViewShadow() {
DispatchQueue.main.asyncAfter(deadline: (.now() + 0.2)) {
let shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 15).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.lightGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 2.6, height: 2.6)
shadowLayer.shadowOpacity = 0.8
shadowLayer.shadowRadius = 8.0
self.layer.insertSublayer(shadowLayer, at: 0)
}
}
}
This is an older question, but I would have just done everything in your custom draw method like below.
I usually will do this if I know I want to apply a drop shadow to my rounded view (which of course means I don't want to use masksToBounds
)
You also don't have to add an extra "shadow view" to the hierarchy.
@IBDesignable
class RoundedView: UIView {
@IBInspectable
var cornerRadius: CGFloat = 0
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// You could use custom IBInspectable attributes
// for the stroke and fill color.
context.setFillColor(UIColor.white.cgColor)
context.setStrokeColor(UIColor.orange.cgColor)
// Add a clipping path to get the rounded look
// you want.
UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
// Fill and stroke your background.
let background = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
background.lineWidth = 2
background.fill()
background.stroke()
}
private func shadow() {
layer.shadowColor = UIColor.black.cgColor
layer.shadowRadius = 5
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize.zero
}
override func awakeFromNib() {
super.awakeFromNib()
shadow()
}
}